diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-02-03 01:29:56 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2024-02-03 01:29:56 +0000 |
commit | b5c5e27ab6bbbaf69203044bab7b9af61efd79dd (patch) | |
tree | d51ac9fb3e2a72b592dde8472dcf2bae54adce13 | |
parent | 041c765ca5ee78a8b8c21db1882740b81439c1eb (diff) | |
parent | 808fc1574781a8be6700feb728c9ee65426722a8 (diff) | |
download | Launcher3-simpleperf-release.tar.gz |
Merge "Snap for 11400057 from ded14cc2110e39408f74abac8a83e0a0f16608d2 to simpleperf-release" into simpleperf-releasesimpleperf-release
734 files changed, 20967 insertions, 11129 deletions
diff --git a/Android.bp b/Android.bp index 787c98bd19..597cc04b74 100644 --- a/Android.bp +++ b/Android.bp @@ -23,42 +23,66 @@ min_launcher3_sdk_version = "26" // All sources are split so they can be reused in many other libraries/apps in other folders filegroup { name: "launcher-src", - srcs: [ "src/**/*.java", "src/**/*.kt" ], + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], } filegroup { name: "launcher-quickstep-src", - srcs: [ "quickstep/src/**/*.java", "quickstep/src/**/*.kt" ], + srcs: [ + "quickstep/src/**/*.java", + "quickstep/src/**/*.kt", + ], } filegroup { name: "launcher-go-src", - srcs: [ "go/src/**/*.java", "go/src/**/*.kt" ], + srcs: [ + "go/src/**/*.java", + "go/src/**/*.kt", + ], } filegroup { name: "launcher-go-quickstep-src", - srcs: [ "go/quickstep/src/**/*.java", "go/quickstep/src/**/*.kt" ], + srcs: [ + "go/quickstep/src/**/*.java", + "go/quickstep/src/**/*.kt", + ], } filegroup { name: "launcher-src_shortcuts_overrides", - srcs: [ "src_shortcuts_overrides/**/*.java", "src_shortcuts_overrides/**/*.kt" ], + srcs: [ + "src_shortcuts_overrides/**/*.java", + "src_shortcuts_overrides/**/*.kt", + ], } filegroup { name: "launcher-src_ui_overrides", - srcs: [ "src_ui_overrides/**/*.java", "src_ui_overrides/**/*.kt" ], + srcs: [ + "src_ui_overrides/**/*.java", + "src_ui_overrides/**/*.kt", + ], } filegroup { name: "launcher-ext_tests", - srcs: [ "ext_tests/**/*.java", "ext_tests/**/*.kt" ], + srcs: [ + "ext_tests/**/*.java", + "ext_tests/**/*.kt", + ], } filegroup { name: "launcher-quickstep-ext_tests", - srcs: [ "quickstep/ext_tests/**/*.java", "quickstep/ext_tests/**/*.kt" ], + srcs: [ + "quickstep/ext_tests/**/*.java", + "quickstep/ext_tests/**/*.kt", + ], } // Proguard files for Launcher3 @@ -85,9 +109,12 @@ android_library { srcs: [ "tests/tapl/**/*.java", ], - resource_dirs: [ ], + resource_dirs: [], manifest: "tests/tapl/AndroidManifest.xml", platform_apis: true, + lint: { + baseline_filename: "lint-baseline.xml", + }, } java_library_static { @@ -99,12 +126,15 @@ java_library_static { sdk_version: "current", proto: { type: "lite", - local_include_dirs:[ + local_include_dirs: [ "protos", "protos_overrides", ], }, static_libs: ["libprotobuf-java-lite"], + lint: { + baseline_filename: "lint-baseline.xml", + }, } java_library_static { @@ -115,14 +145,17 @@ java_library_static { sdk_version: "current", proto: { type: "lite", - local_include_dirs:[ + local_include_dirs: [ "quickstep/protos_overrides", ], }, static_libs: [ - "libprotobuf-java-lite", - "launcher_log_protos_lite" - ], + "libprotobuf-java-lite", + "launcher_log_protos_lite", + ], + lint: { + baseline_filename: "lint-baseline.xml", + }, } java_library { @@ -134,12 +167,15 @@ java_library { sdk_version: "current", min_sdk_version: min_launcher3_sdk_version, + lint: { + baseline_filename: "lint-baseline.xml", + }, } // Library with all the dependencies for building Launcher3 android_library { name: "Launcher3ResLib", - srcs: [ ], + srcs: [], resource_dirs: ["res"], static_libs: [ "LauncherPluginLib", @@ -153,7 +189,8 @@ android_library { "androidx.cardview_cardview", "com.google.android.material_material", "iconloader_base", - "view_capture" + "view_capture", + "animationlib", ], manifest: "AndroidManifest-common.xml", sdk_version: "current", @@ -172,6 +209,7 @@ android_library { static_libs: [ "Launcher3ResLib", "launcher-testing-shared", + "animationlib", ], sdk_version: "current", min_sdk_version: min_launcher3_sdk_version, @@ -231,7 +269,7 @@ android_app { // Library with all the dependencies for building quickstep android_library { name: "QuickstepResLib", - srcs: [ ], + srcs: [], resource_dirs: [ "quickstep/res", ], @@ -247,9 +285,11 @@ android_library { ], manifest: "quickstep/AndroidManifest.xml", min_sdk_version: "current", + lint: { + baseline_filename: "lint-baseline.xml", + }, } - // Library with all the dependencies for building Launcher Go android_library { name: "LauncherGoResLib", @@ -354,7 +394,10 @@ android_app { manifest: "go/AndroidManifest.xml", jacoco: { include_filter: ["com.android.launcher3.*"], - } + }, + lint: { + baseline_filename: "lint-baseline.xml", + }, } @@ -390,7 +433,10 @@ android_app { manifest: "quickstep/AndroidManifest.xml", jacoco: { include_filter: ["com.android.launcher3.*"], - } + }, + lint: { + baseline_filename: "lint-baseline.xml", + }, } @@ -408,7 +454,7 @@ android_app { min_sdk_version: "current", target_sdk_version: "current", - srcs: [ ], + srcs: [], resource_dirs: [ "go/quickstep/res", @@ -440,7 +486,9 @@ android_app { manifest: "quickstep/AndroidManifest.xml", jacoco: { include_filter: ["com.android.launcher3.*"], - } + }, + lint: { + baseline_filename: "lint-baseline.xml", + }, } - @@ -13,6 +13,9 @@ vadimt@google.com winsonc@google.com jonmiranda@google.com alexchau@google.com +patmanning@google.com +tsuharesu@google.com +awickham@google.com per-file FeatureFlags.java, globs = set noparent per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com, captaincole@google.com diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java index e5b2c678ad..29b24b797c 100644 --- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java +++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java @@ -25,14 +25,13 @@ import android.content.Context; import android.os.Binder; import android.os.Bundle; import android.system.Os; -import android.util.Log; import androidx.annotation.Keep; import androidx.annotation.Nullable; import com.android.launcher3.BubbleTextView; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherSettings; +import com.android.launcher3.LauncherModel; import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.testing.shared.TestProtocol; @@ -62,7 +61,6 @@ public class DebugTestInformationHandler extends TestInformationHandler { public void onActivityCreated(Activity activity, Bundle bundle) { sActivities.put(activity, true); ++sActivitiesCreatedCount; - Log.d(TestProtocol.FLAKY_ACTIVITY_COUNT, "onActivityCreated", new Exception()); } @Override @@ -188,13 +186,29 @@ public class DebugTestInformationHandler extends TestInformationHandler { return response; } + case TestProtocol.REQUEST_REINITIALIZE_DATA: { + final long identity = Binder.clearCallingIdentity(); + try { + MODEL_EXECUTOR.execute(() -> { + LauncherModel model = LauncherAppState.getInstance(mContext).getModel(); + model.getModelDbController().createEmptyDB(); + MAIN_EXECUTOR.execute(model::forceReload); + }); + return response; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + case TestProtocol.REQUEST_CLEAR_DATA: { final long identity = Binder.clearCallingIdentity(); try { - LauncherSettings.Settings.call(mContext.getContentResolver(), - LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); - MAIN_EXECUTOR.submit(() -> - LauncherAppState.getInstance(mContext).getModel().forceReload()); + MODEL_EXECUTOR.execute(() -> { + LauncherModel model = LauncherAppState.getInstance(mContext).getModel(); + model.getModelDbController().createEmptyDB(); + model.getModelDbController().clearEmptyDbFlag(); + MAIN_EXECUTOR.execute(model::forceReload); + }); return response; } finally { Binder.restoreCallingIdentity(identity); diff --git a/go/quickstep/res/values-am/strings.xml b/go/quickstep/res/values-am/strings.xml index ffc59d931d..ed3479783a 100644 --- a/go/quickstep/res/values-am/strings.xml +++ b/go/quickstep/res/values-am/strings.xml @@ -9,12 +9,12 @@ <string name="dialog_cancel" msgid="6464336969134856366">"ይቅር"</string> <string name="dialog_settings" msgid="6564397136021186148">"ቅንብሮች"</string> <string name="niu_actions_confirmation_title" msgid="3863451714863526143">"በማያ ገጹ ላይ ጽሑፍን ይተረጉሙ ወይም ያዳምጡ"</string> - <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"እንደ በማያ ገፅዎ ላይ ያለ ጽሁፍ፣ የድር አድራሻዎች እና ቅጽበታዊ ገፅ እይታዎች ያሉ መረጃዎች ለGoogle ሊጋሩ ይችላሉ።\n\nምን መረጃ እንደሚያጋሩ ለመቀየር ወደ "<b>"ቅንብሮች > መተግበሪያዎች > ነባሪ መተግበሪያዎች > ዲጂታል ረዳት መተግበሪያ"</b>" ይሂዱ።"</string> + <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"እንደ በማያ ገጽዎ ላይ ያለ ጽሁፍ፣ የድር አድራሻዎች እና ቅጽበታዊ ገጽ እይታዎች ያሉ መረጃዎች ለGoogle ሊጋሩ ይችላሉ።\n\nምን መረጃ እንደሚያጋሩ ለመቀየር ወደ "<b>"ቅንብሮች > መተግበሪያዎች > ነባሪ መተግበሪያዎች > ዲጂታል ረዳት መተግበሪያ"</b>" ይሂዱ።"</string> <string name="assistant_not_selected_title" msgid="5017072974603345228">"ይህንን ባህሪ ለመጠቀም ረዳት ይምረጡ"</string> <string name="assistant_not_selected_text" msgid="3244613673884359276">"በማያ ገጽዎ ላይ ጽሑፍን ለማዳመጥ ወይም ለመተርጎም በቅንብሮች ውስጥ የዲጂታል ረዳት መተግበሪያን ይምረጡ"</string> <string name="assistant_not_supported_title" msgid="1675788067597484142">"ይህንን ባህሪ ለመጠቀም ረዳትዎን ይቀይሩ"</string> <string name="assistant_not_supported_text" msgid="1708031078549268884">"በማያ ገጽዎ ላይ ጽሑፍን ለማዳመጥ ወይም ለመተርጎም በቅንብሮች ውስጥ የዲጂታል ረዳት መተግበሪያዎን ይቀይሩ"</string> - <string name="tooltip_listen" msgid="7634466447860989102">"በዚህ ማያ ገፅ ላይ ጽሁፍ ለማዳመጥ እዚህ መታ ያድርጉ"</string> - <string name="tooltip_translate" msgid="4184845868901542567">"በዚህ ማያ ገፅ ላይ ጽሁፍ ለመተርጎም እዚህ መታ ያድርጉ"</string> + <string name="tooltip_listen" msgid="7634466447860989102">"በዚህ ማያ ገጽ ላይ ጽሁፍ ለማዳመጥ እዚህ መታ ያድርጉ"</string> + <string name="tooltip_translate" msgid="4184845868901542567">"በዚህ ማያ ገጽ ላይ ጽሁፍ ለመተርጎም እዚህ መታ ያድርጉ"</string> <string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"ይህ መተግበሪያ ሊጋራ አይችልም"</string> </resources> diff --git a/go/quickstep/res/values-ky/strings.xml b/go/quickstep/res/values-ky/strings.xml index dcc1e4ea77..55e70c8cdf 100644 --- a/go/quickstep/res/values-ky/strings.xml +++ b/go/quickstep/res/values-ky/strings.xml @@ -11,9 +11,9 @@ <string name="niu_actions_confirmation_title" msgid="3863451714863526143">"Экрандагы текстти которуу же угуу"</string> <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"Экрандагы текст, веб-даректер жана скриншоттор сыяктуу маалымат Google менен бөлүшүлүшү мүмкүн.\n\nБөлүшүлгөн маалыматты өзгөртүү үчүн"<b>"Параметрлер > Колдонмолор > Демейки колдонмолор > Санариптик жардамчы колдонмосуна өтүңүз"</b>"."</string> <string name="assistant_not_selected_title" msgid="5017072974603345228">"Бул функцияны колдонуу үчүн жардамчыны тандаңыз"</string> - <string name="assistant_not_selected_text" msgid="3244613673884359276">"Экраныңыздагы текстти угуу же которуу үчүн Параметрлерден санариптик жардамчы колдонмосун тандаңыз"</string> + <string name="assistant_not_selected_text" msgid="3244613673884359276">"Экраныңыздагы текстти угуу же которуу үчүн Жөндөөлөрдөн санариптик жардамчы колдонмосун тандаңыз"</string> <string name="assistant_not_supported_title" msgid="1675788067597484142">"Бул функцияны колдонуу үчүн жардамчыңызды өзгөртүңүз"</string> - <string name="assistant_not_supported_text" msgid="1708031078549268884">"Экраныңыздагы текстти угуу же которуу үчүн Параметрлерден санариптик жардамчы колдонмосун өзгөртүңүз"</string> + <string name="assistant_not_supported_text" msgid="1708031078549268884">"Экраныңыздагы текстти угуу же которуу үчүн Жөндөөлөрдөн санариптик жардамчы колдонмосун өзгөртүңүз"</string> <string name="tooltip_listen" msgid="7634466447860989102">"Бул экрандагы текстти угуу үчүн бул жерди басыңыз"</string> <string name="tooltip_translate" msgid="4184845868901542567">"Бул экрандагы текстти которуу үчүн бул жерди басыңыз"</string> <string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"Бул колдонмону бөлүшүүгө болбойт"</string> diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto index 63ea20c73d..f8b08f86c0 100644 --- a/protos/launcher_atom.proto +++ b/protos/launcher_atom.proto @@ -135,7 +135,7 @@ message TaskBarContainer { } } -// Next value 51 +// Next value 52 enum Attribute { option allow_alias = true; @@ -187,6 +187,7 @@ enum Attribute { ALL_APPS_SEARCH_RESULT_SYSTEM_POINTER = 42; ALL_APPS_SEARCH_RESULT_EDUCARD = 43; ALL_APPS_SEARCH_RESULT_LOCATION = 50; + ALL_APPS_SEARCH_RESULT_TEXT_HEADER = 51; // Result sources DATA_SOURCE_APPSEARCH_APP_PREVIEW = 45; diff --git a/quickstep/Android.bp b/quickstep/Android.bp index f5a8253563..638ce27800 100644 --- a/quickstep/Android.bp +++ b/quickstep/Android.bp @@ -42,5 +42,6 @@ filegroup { "tests/src/com/android/quickstep/NavigationModeSwitchRule.java", "tests/src/com/android/quickstep/AbstractQuickStepTest.java", "tests/src/com/android/quickstep/TaplTestsQuickstep.java", + "tests/src/com/android/quickstep/TaplTestsSplitscreen.java", ] } diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml index 7d7054f5a5..c6e2d8cb74 100644 --- a/quickstep/AndroidManifest-launcher.xml +++ b/quickstep/AndroidManifest-launcher.xml @@ -20,7 +20,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.launcher3"> - <uses-sdk android:targetSdkVersion="33" android:minSdkVersion="26"/> <!-- Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml. Refer comments around specific entries on how to extend individual components. diff --git a/quickstep/protos_overrides/launcher_atom_extension.proto b/quickstep/protos_overrides/launcher_atom_extension.proto index f5a277bb95..b3df353913 100644 --- a/quickstep/protos_overrides/launcher_atom_extension.proto +++ b/quickstep/protos_overrides/launcher_atom_extension.proto @@ -61,6 +61,9 @@ message DeviceSearchResultContainer{ // User entered by tapping on QSB bar on homescreen. QSB = 2; + + // User entered by swiping up from overview (using Rocket Gesture). + OVERVIEW = 3; } } } diff --git a/res/interpolator/standard_accelerate.xml b/quickstep/res/drawable/bg_bubble_dismiss_circle.xml index 394393dc36..b793eecdf9 100644 --- a/res/interpolator/standard_accelerate.xml +++ b/quickstep/res/drawable/bg_bubble_dismiss_circle.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2022 The Android Open Source Project + ~ Copyright (C) 2023 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -15,8 +15,13 @@ ~ limitations under the License. --> -<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" - android:controlX1="0.3" - android:controlY1="0" - android:controlX2="1" - android:controlY2="1"/>
\ No newline at end of file +<shape + xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + + <stroke + android:width="2dp" + android:color="@android:color/system_accent1_600" /> + + <solid android:color="@android:color/system_accent1_600" /> +</shape>
\ No newline at end of file diff --git a/quickstep/res/drawable/bg_floating_desktop_select.xml b/quickstep/res/drawable/bg_floating_desktop_select.xml new file mode 100644 index 0000000000..d7df338e93 --- /dev/null +++ b/quickstep/res/drawable/bg_floating_desktop_select.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<shape + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:shape="rectangle"> + + <corners android:radius="@dimen/rounded_button_radius" /> + <solid android:color="?androidprv:attr/materialColorPrimaryContainer" /> +</shape>
\ No newline at end of file diff --git a/quickstep/res/drawable/ic_bubble_dismiss_white.xml b/quickstep/res/drawable/ic_bubble_dismiss_white.xml new file mode 100644 index 0000000000..b15111b821 --- /dev/null +++ b/quickstep/res/drawable/ic_bubble_dismiss_white.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="20dp" + android:height="20dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z" + android:fillColor="@android:color/system_neutral1_50"/> +</vector> diff --git a/res/drawable/top_rounded_popup_ripple.xml b/quickstep/res/drawable/taskbar_divider_button.xml index 7468480192..cb116cfece 100644 --- a/res/drawable/top_rounded_popup_ripple.xml +++ b/quickstep/res/drawable/taskbar_divider_button.xml @@ -13,15 +13,18 @@ See the License for the specific language governing permissions and limitations under the License. --> -<ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:color="?android:attr/colorControlHighlight"> - <item android:id="@android:id/mask"> - <shape android:shape="rectangle"> - <solid android:color="#FFFFFFFF"/> - <corners android:bottomLeftRadius="0dp" - android:bottomRightRadius="0dp" - android:topLeftRadius="@dimen/dialogCornerRadius" - android:topRightRadius="@dimen/dialogCornerRadius"/> - </shape> - </item> -</ripple>
\ No newline at end of file + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="52dp" + android:height="52dp" + android:viewportHeight="52" + android:viewportWidth="52"> + <group> + <path + android:fillColor="@color/taskbar_divider_background" + android:pathData="M26,11L26,41" + android:strokeColor="@color/taskbar_divider_background" + android:strokeLineCap="round" + android:strokeWidth="2" /> + </group> +</vector>
\ No newline at end of file diff --git a/quickstep/res/interpolator/three_point_fast_out_extra_slow_in.xml b/quickstep/res/interpolator/three_point_fast_out_extra_slow_in.xml deleted file mode 100644 index 70c4231140..0000000000 --- a/quickstep/res/interpolator/three_point_fast_out_extra_slow_in.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2021, 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. -*/ ---> - -<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" - android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/> diff --git a/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml b/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml index 4e676298c8..69e157433a 100644 --- a/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml +++ b/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml @@ -23,7 +23,7 @@ android:importantForAccessibility="yes" android:background="@drawable/keyboard_quick_switch_task_view_background" android:clipToOutline="true" - launcher:borderColor="?androidprv:attr/materialColorOutline"> + launcher:focusBorderColor="?androidprv:attr/materialColorOutline"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/content" diff --git a/quickstep/res/layout-sw600dp-land/gesture_tutorial_step_menu.xml b/quickstep/res/layout-sw600dp-land/gesture_tutorial_step_menu.xml index 225b4cdc39..672440f3e7 100644 --- a/quickstep/res/layout-sw600dp-land/gesture_tutorial_step_menu.xml +++ b/quickstep/res/layout-sw600dp-land/gesture_tutorial_step_menu.xml @@ -13,155 +13,163 @@ See the License for the specific language governing permissions and limitations under the License. --> -<androidx.constraintlayout.widget.ConstraintLayout +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:theme="@style/GestureTutorialActivity" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingTop="@dimen/gesture_tutorial_menu_padding_top" - android:paddingBottom="@dimen/gesture_tutorial_menu_padding_bottom" - android:paddingHorizontal="@dimen/gesture_tutorial_menu_padding_horizontal" - android:background="?androidprv:attr/materialColorSurfaceContainer"> + android:background="?androidprv:attr/materialColorSurfaceContainer" + android:fitsSystemWindows="true"> <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/gesture_tutorial_menu_home_button" - android:layout_width="0dp" - android:layout_height="@dimen/gesture_tutorial_menu_button_height" - android:layout_marginEnd="@dimen/gesture_tutorial_menu_button_spacing" - android:layout_marginBottom="24dp" - android:background="@drawable/gesture_tutorial_menu_home_button_background" - android:clipToOutline="true" - - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toTopOf="@id/guideline" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toStartOf="@id/gesture_tutorial_menu_back_button"> - - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/gesture_tutorial_home_step_shape" - android:scaleType="fitXY" - android:adjustViewBounds="true" - - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent"/> - - <TextView - style="@style/TextAppearance.GestureTutorial.MenuButton.Home" - android:id="@+id/gesture_tutorial_menu_home_button_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/home_gesture_tutorial_title" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingTop="@dimen/gesture_tutorial_menu_padding_top" + android:paddingBottom="@dimen/gesture_tutorial_menu_padding_bottom" + android:paddingHorizontal="@dimen/gesture_tutorial_menu_padding_horizontal" + android:clipToPadding="false"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/gesture_tutorial_menu_home_button" + android:layout_width="0dp" + android:layout_height="@dimen/gesture_tutorial_menu_button_height" + android:layout_marginEnd="@dimen/gesture_tutorial_menu_button_spacing" + android:layout_marginBottom="24dp" + android:background="@drawable/gesture_tutorial_menu_home_button_background" + android:clipToOutline="true" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toTopOf="@id/guideline" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent"/> - - </androidx.constraintlayout.widget.ConstraintLayout> - - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/gesture_tutorial_menu_back_button" - android:layout_width="0dp" - android:layout_height="@dimen/gesture_tutorial_menu_button_height" - android:layout_marginEnd="@dimen/gesture_tutorial_menu_button_spacing" - android:layout_marginBottom="24dp" - android:background="@drawable/gesture_tutorial_menu_back_button_background" - android:clipToOutline="true" - - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toTopOf="@id/guideline" - app:layout_constraintStart_toEndOf="@id/gesture_tutorial_menu_home_button" - app:layout_constraintEnd_toStartOf="@id/gesture_tutorial_menu_overview_button"> - - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/gesture_tutorial_back_step_shape" - android:layout_marginBottom="@dimen/gesture_tutorial_menu_back_shape_bottom_margin" - android:scaleType="fitXY" - android:adjustViewBounds="true" - - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toStartOf="parent"/> - - <TextView - style="@style/TextAppearance.GestureTutorial.MenuButton.Back" - android:id="@+id/gesture_tutorial_menu_back_button_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/back_gesture_tutorial_title" + app:layout_constraintEnd_toStartOf="@id/gesture_tutorial_menu_back_button"> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_tutorial_home_step_shape" + android:scaleType="fitXY" + android:adjustViewBounds="true" + + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> + + <TextView + style="@style/TextAppearance.GestureTutorial.MenuButton.Home" + android:id="@+id/gesture_tutorial_menu_home_button_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/home_gesture_tutorial_title" + + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> + + </androidx.constraintlayout.widget.ConstraintLayout> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/gesture_tutorial_menu_back_button" + android:layout_width="0dp" + android:layout_height="@dimen/gesture_tutorial_menu_button_height" + android:layout_marginEnd="@dimen/gesture_tutorial_menu_button_spacing" + android:layout_marginBottom="24dp" + android:background="@drawable/gesture_tutorial_menu_back_button_background" + android:clipToOutline="true" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent"/> - - </androidx.constraintlayout.widget.ConstraintLayout> + app:layout_constraintBottom_toTopOf="@id/guideline" + app:layout_constraintStart_toEndOf="@id/gesture_tutorial_menu_home_button" + app:layout_constraintEnd_toStartOf="@id/gesture_tutorial_menu_overview_button"> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_tutorial_back_step_shape" + android:layout_marginBottom="@dimen/gesture_tutorial_menu_back_shape_bottom_margin" + android:scaleType="fitXY" + android:adjustViewBounds="true" + + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent"/> + + <TextView + style="@style/TextAppearance.GestureTutorial.MenuButton.Back" + android:id="@+id/gesture_tutorial_menu_back_button_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/back_gesture_tutorial_title" + + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> + + </androidx.constraintlayout.widget.ConstraintLayout> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/gesture_tutorial_menu_overview_button" + android:layout_width="0dp" + android:layout_height="@dimen/gesture_tutorial_menu_button_height" + android:layout_marginBottom="24dp" + android:background="@drawable/gesture_tutorial_menu_overview_button_background" + android:clipToOutline="true" - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/gesture_tutorial_menu_overview_button" - android:layout_width="0dp" - android:layout_height="@dimen/gesture_tutorial_menu_button_height" - android:layout_marginBottom="24dp" - android:background="@drawable/gesture_tutorial_menu_overview_button_background" - android:clipToOutline="true" - - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toTopOf="@id/guideline" - app:layout_constraintStart_toEndOf="@id/gesture_tutorial_menu_back_button" - app:layout_constraintEnd_toEndOf="parent"> - - <ImageView + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@id/guideline" + app:layout_constraintStart_toEndOf="@id/gesture_tutorial_menu_back_button" + app:layout_constraintEnd_toEndOf="parent"> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_tutorial_overview_step_shape" + android:scaleType="fitXY" + android:adjustViewBounds="true" + + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> + + <TextView + style="@style/TextAppearance.GestureTutorial.MenuButton.Overview" + android:id="@+id/gesture_tutorial_menu_overview_button_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/overview_gesture_tutorial_title" + + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> + + </androidx.constraintlayout.widget.ConstraintLayout> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:src="@drawable/gesture_tutorial_overview_step_shape" - android:scaleType="fitXY" - android:adjustViewBounds="true" + android:orientation="horizontal" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent"/> + app:layout_constraintGuide_end="@dimen/gesture_tutorial_menu_done_button_spacing"/> - <TextView - style="@style/TextAppearance.GestureTutorial.MenuButton.Overview" - android:id="@+id/gesture_tutorial_menu_overview_button_text" + <Button + style="@style/TextAppearance.GestureTutorial.ButtonLabel" + android:id="@+id/gesture_tutorial_menu_done_button" android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/overview_gesture_tutorial_title" - - app:layout_constraintTop_toTopOf="parent" + android:layout_height="40dp" + android:layout_marginVertical="16dp" + android:text="@string/gesture_tutorial_action_button_label" + android:background="@drawable/gesture_tutorial_action_button_background" + android:backgroundTint="?androidprv:attr/materialColorPrimary" + android:stateListAnimator="@null" + + app:layout_constraintTop_toBottomOf="@id/guideline" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout> - <androidx.constraintlayout.widget.Guideline - android:id="@+id/guideline" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - - app:layout_constraintGuide_end="@dimen/gesture_tutorial_menu_done_button_spacing"/> - - <Button - style="@style/TextAppearance.GestureTutorial.ButtonLabel" - android:id="@+id/gesture_tutorial_menu_done_button" - android:layout_width="wrap_content" - android:layout_height="40dp" - android:layout_marginVertical="16dp" - android:text="@string/gesture_tutorial_action_button_label" - android:background="@drawable/gesture_tutorial_action_button_background" - android:backgroundTint="?androidprv:attr/materialColorPrimary" - android:stateListAnimator="@null" - - app:layout_constraintTop_toBottomOf="@id/guideline" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent"/> - -</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file +</FrameLayout>
\ No newline at end of file diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml index 2c312a7e33..685a15180b 100644 --- a/quickstep/res/layout/activity_allset.xml +++ b/quickstep/res/layout/activity_allset.xml @@ -41,6 +41,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" + android:fitsSystemWindows="true" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" @@ -95,7 +96,6 @@ style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginBottom="24dp" android:text="@string/allset_hint" android:textSize="@dimen/allset_page_swipe_up_text_size" android:gravity="center_horizontal" diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml index bfeb82d9f9..f0ea09c1af 100644 --- a/quickstep/res/layout/fallback_recents_activity.xml +++ b/quickstep/res/layout/fallback_recents_activity.xml @@ -15,6 +15,7 @@ --> <com.android.launcher3.LauncherRootView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/launcher" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> diff --git a/quickstep/res/layout/floating_desktop_app_select.xml b/quickstep/res/layout/floating_desktop_app_select.xml new file mode 100644 index 0000000000..375fc44b73 --- /dev/null +++ b/quickstep/res/layout/floating_desktop_app_select.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<com.android.quickstep.views.DesktopAppSelectView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:layout_width="wrap_content" + android:layout_height="@dimen/desktop_mode_floating_app_select_height" + android:layout_gravity="top|center_horizontal" + android:background="@drawable/bg_floating_desktop_select" + android:elevation="@dimen/desktop_mode_floating_app_select_elevation" + android:gravity="center_vertical" + android:orientation="horizontal"> + + <TextView + android:id="@+id/desktop_app_select_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/desktop_mode_floating_app_select_text_margin" + android:layout_marginStart="@dimen/desktop_mode_floating_app_select_margin" + android:drawablePadding="@dimen/desktop_mode_floating_app_select_text_margin" + android:drawableStart="@drawable/ic_desktop" + android:drawableTint="?androidprv:attr/materialColorOnPrimaryContainer" + android:fontFamily="google-sans-medium" + android:gravity="center_vertical" + android:text="@string/desktop_select_app_toast" + android:textColor="?androidprv:attr/materialColorOnPrimaryContainer" + android:textSize="@dimen/desktop_mode_floating_app_select_text_size" /> + + <Button + android:id="@+id/close_button" + style="@android:style/Widget.DeviceDefault.Button.Borderless" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/desktop_mode_floating_app_select_margin" + android:minWidth="0dp" + android:fontFamily="google-sans-medium" + android:text="@string/desktop_button_close_app_toast" + android:textAllCaps="false" + android:textColor="?androidprv:attr/materialColorPrimary" + android:textSize="@dimen/desktop_mode_floating_app_select_text_size" /> + +</com.android.quickstep.views.DesktopAppSelectView> diff --git a/quickstep/res/layout/gesture_tutorial_activity.xml b/quickstep/res/layout/gesture_tutorial_activity.xml index 0e763ec734..82caedf45f 100644 --- a/quickstep/res/layout/gesture_tutorial_activity.xml +++ b/quickstep/res/layout/gesture_tutorial_activity.xml @@ -13,80 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. --> -<androidx.constraintlayout.widget.ConstraintLayout +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/gesture_tutorial_fragment_container" android:layout_width="match_parent" - android:layout_height="match_parent"> - - <FrameLayout - android:id="@+id/gesture_tutorial_fragment_container" - android:layout_width="match_parent" - android:layout_height="match_parent" - - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" /> - - <RelativeLayout - android:id="@+id/rotation_prompt" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="?attr/surfaceHome" - android:visibility="gone" - - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent"> - - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/background" - android:layout_width="300dp" - android:layout_height="wrap_content" - android:layout_centerHorizontal="true" - android:layout_centerVertical="true" - android:background="@drawable/rotate_prompt_bg" - android:padding="24dp"> - - <ImageView - android:id="@+id/icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/rotate_tutorial_warning" - - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintEnd_toEndOf="parent" /> - - <TextView - android:id="@+id/rotate_title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="20dp" - android:lineSpacingExtra="2sp" - android:text="@string/gesture_tutorial_rotation_prompt_title" - android:textAppearance="@style/rotate_prompt_title" - - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/icon" - app:layout_constraintEnd_toEndOf="parent" /> - - <TextView - android:id="@+id/rotate_screen_subtitle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="16dp" - android:gravity="center" - android:text="@string/gesture_tutorial_rotation_prompt" - android:textAppearance="@style/rotate_prompt_subtitle" - - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/rotate_title" - app:layout_constraintEnd_toEndOf="parent" /> - - </androidx.constraintlayout.widget.ConstraintLayout> - </RelativeLayout> - -</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file + android:layout_height="match_parent"/>
\ No newline at end of file diff --git a/quickstep/res/layout/gesture_tutorial_rotation_prompt.xml b/quickstep/res/layout/gesture_tutorial_rotation_prompt.xml new file mode 100644 index 0000000000..b41a96dd06 --- /dev/null +++ b/quickstep/res/layout/gesture_tutorial_rotation_prompt.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/surfaceHome"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/background" + android:layout_width="300dp" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" + android:background="@drawable/rotate_prompt_bg" + android:padding="24dp" + + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent"> + + <ImageView + android:id="@+id/icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/rotate_tutorial_warning" + + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintEnd_toEndOf="parent" /> + + <TextView + android:id="@+id/rotate_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="20dp" + android:lineSpacingExtra="2sp" + android:text="@string/gesture_tutorial_rotation_prompt_title" + android:textAppearance="@style/rotate_prompt_title" + + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/icon" + app:layout_constraintEnd_toEndOf="parent" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:gravity="center" + android:text="@string/gesture_tutorial_rotation_prompt" + android:textAppearance="@style/rotate_prompt_subtitle" + + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/rotate_title" + app:layout_constraintEnd_toEndOf="parent" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/quickstep/res/layout/gesture_tutorial_step_menu.xml b/quickstep/res/layout/gesture_tutorial_step_menu.xml index cf78b1b016..c8ee6e9322 100644 --- a/quickstep/res/layout/gesture_tutorial_step_menu.xml +++ b/quickstep/res/layout/gesture_tutorial_step_menu.xml @@ -13,154 +13,161 @@ See the License for the specific language governing permissions and limitations under the License. --> -<androidx.constraintlayout.widget.ConstraintLayout +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:theme="@style/GestureTutorialActivity" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingTop="@dimen/gesture_tutorial_menu_padding_top" - android:paddingBottom="@dimen/gesture_tutorial_menu_padding_bottom" - android:paddingHorizontal="@dimen/gesture_tutorial_menu_padding_horizontal" android:background="?androidprv:attr/materialColorSurfaceContainer" - android:clipToPadding="false"> + android:fitsSystemWindows="true"> <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/gesture_tutorial_menu_home_button" android:layout_width="match_parent" - android:layout_height="@dimen/gesture_tutorial_menu_button_height" - android:background="@drawable/gesture_tutorial_menu_home_button_background" - android:clipToOutline="true" - app:layout_constraintVertical_chainStyle="packed" - - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toTopOf="@id/gesture_tutorial_menu_back_button" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent"> - - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/gesture_tutorial_home_step_shape" - android:scaleType="fitXY" - android:adjustViewBounds="true" - - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent"/> - - <TextView - style="@style/TextAppearance.GestureTutorial.MenuButton.Home" - android:id="@+id/gesture_tutorial_menu_home_button_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/home_gesture_tutorial_title" + android:layout_height="match_parent" + android:paddingTop="@dimen/gesture_tutorial_menu_padding_top" + android:paddingBottom="@dimen/gesture_tutorial_menu_padding_bottom" + android:paddingHorizontal="@dimen/gesture_tutorial_menu_padding_horizontal" + android:clipToPadding="false"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/gesture_tutorial_menu_home_button" + android:layout_width="match_parent" + android:layout_height="@dimen/gesture_tutorial_menu_button_height" + android:background="@drawable/gesture_tutorial_menu_home_button_background" + android:clipToOutline="true" + app:layout_constraintVertical_chainStyle="packed" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toTopOf="@id/gesture_tutorial_menu_back_button" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent"/> - - </androidx.constraintlayout.widget.ConstraintLayout> - - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/gesture_tutorial_menu_back_button" - android:layout_width="match_parent" - android:layout_height="@dimen/gesture_tutorial_menu_button_height" - android:layout_marginTop="@dimen/gesture_tutorial_menu_button_spacing" - android:background="@drawable/gesture_tutorial_menu_back_button_background" - android:clipToOutline="true" - - app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_menu_home_button" - app:layout_constraintBottom_toTopOf="@id/gesture_tutorial_menu_overview_button" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent"> - - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/gesture_tutorial_back_step_shape" - android:scaleType="fitXY" - android:adjustViewBounds="true" - - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toStartOf="parent"/> - - <TextView - style="@style/TextAppearance.GestureTutorial.MenuButton.Back" - android:id="@+id/gesture_tutorial_menu_back_button_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/back_gesture_tutorial_title" - - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent"> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_tutorial_home_step_shape" + android:scaleType="fitXY" + android:adjustViewBounds="true" + + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> + + <TextView + style="@style/TextAppearance.GestureTutorial.MenuButton.Home" + android:id="@+id/gesture_tutorial_menu_home_button_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/home_gesture_tutorial_title" + + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> + + </androidx.constraintlayout.widget.ConstraintLayout> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/gesture_tutorial_menu_back_button" + android:layout_width="match_parent" + android:layout_height="@dimen/gesture_tutorial_menu_button_height" + android:layout_marginTop="@dimen/gesture_tutorial_menu_button_spacing" + android:background="@drawable/gesture_tutorial_menu_back_button_background" + android:clipToOutline="true" + + app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_menu_home_button" + app:layout_constraintBottom_toTopOf="@id/gesture_tutorial_menu_overview_button" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent"/> - - </androidx.constraintlayout.widget.ConstraintLayout> - - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/gesture_tutorial_menu_overview_button" - android:layout_width="match_parent" - android:layout_height="@dimen/gesture_tutorial_menu_button_height" - android:layout_marginTop="@dimen/gesture_tutorial_menu_button_spacing" - android:background="@drawable/gesture_tutorial_menu_overview_button_background" - android:clipToOutline="true" - - app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_menu_back_button" - app:layout_constraintBottom_toTopOf="@id/guideline" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent"> - - <ImageView + app:layout_constraintEnd_toEndOf="parent"> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_tutorial_back_step_shape" + android:scaleType="fitXY" + android:adjustViewBounds="true" + + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent"/> + + <TextView + style="@style/TextAppearance.GestureTutorial.MenuButton.Back" + android:id="@+id/gesture_tutorial_menu_back_button_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/back_gesture_tutorial_title" + + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> + + </androidx.constraintlayout.widget.ConstraintLayout> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/gesture_tutorial_menu_overview_button" + android:layout_width="match_parent" + android:layout_height="@dimen/gesture_tutorial_menu_button_height" + android:layout_marginTop="@dimen/gesture_tutorial_menu_button_spacing" + android:background="@drawable/gesture_tutorial_menu_overview_button_background" + android:clipToOutline="true" + + app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_menu_back_button" + app:layout_constraintBottom_toTopOf="@id/guideline" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_tutorial_overview_step_shape" + android:scaleType="fitXY" + android:adjustViewBounds="true" + + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> + + <TextView + style="@style/TextAppearance.GestureTutorial.MenuButton.Overview" + android:id="@+id/gesture_tutorial_menu_overview_button_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/overview_gesture_tutorial_title" + + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> + + </androidx.constraintlayout.widget.ConstraintLayout> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:src="@drawable/gesture_tutorial_overview_step_shape" - android:scaleType="fitXY" - android:adjustViewBounds="true" + android:orientation="horizontal" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent"/> + app:layout_constraintGuide_end="@dimen/gesture_tutorial_menu_done_button_spacing"/> - <TextView - style="@style/TextAppearance.GestureTutorial.MenuButton.Overview" - android:id="@+id/gesture_tutorial_menu_overview_button_text" + <Button + style="@style/TextAppearance.GestureTutorial.ButtonLabel" + android:id="@+id/gesture_tutorial_menu_done_button" android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/overview_gesture_tutorial_title" - - app:layout_constraintTop_toTopOf="parent" + android:layout_height="40dp" + android:layout_marginVertical="16dp" + android:text="@string/gesture_tutorial_action_button_label" + android:background="@drawable/gesture_tutorial_action_button_background" + android:backgroundTint="?androidprv:attr/materialColorPrimary" + android:stateListAnimator="@null" + + app:layout_constraintTop_toBottomOf="@id/guideline" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout> - <androidx.constraintlayout.widget.Guideline - android:id="@+id/guideline" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - - app:layout_constraintGuide_end="@dimen/gesture_tutorial_menu_done_button_spacing"/> - - <Button - style="@style/TextAppearance.GestureTutorial.ButtonLabel" - android:id="@+id/gesture_tutorial_menu_done_button" - android:layout_width="wrap_content" - android:layout_height="40dp" - android:layout_marginVertical="16dp" - android:text="@string/gesture_tutorial_action_button_label" - android:background="@drawable/gesture_tutorial_action_button_background" - android:backgroundTint="?androidprv:attr/materialColorPrimary" - android:stateListAnimator="@null" - - app:layout_constraintTop_toBottomOf="@id/guideline" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent"/> - -</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file +</FrameLayout>
\ No newline at end of file diff --git a/quickstep/res/layout/keyboard_quick_switch_overview.xml b/quickstep/res/layout/keyboard_quick_switch_overview.xml index e7b1f235a9..4a9b0235ad 100644 --- a/quickstep/res/layout/keyboard_quick_switch_overview.xml +++ b/quickstep/res/layout/keyboard_quick_switch_overview.xml @@ -22,7 +22,7 @@ android:layout_height="@dimen/keyboard_quick_switch_taskview_height" android:clipToOutline="true" android:importantForAccessibility="yes" - launcher:borderColor="?androidprv:attr/materialColorOutline"> + launcher:focusBorderColor="?androidprv:attr/materialColorOutline"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/content" diff --git a/quickstep/res/layout/keyboard_quick_switch_taskview.xml b/quickstep/res/layout/keyboard_quick_switch_taskview.xml index 4d213fadbb..6ed3c6ede5 100644 --- a/quickstep/res/layout/keyboard_quick_switch_taskview.xml +++ b/quickstep/res/layout/keyboard_quick_switch_taskview.xml @@ -23,7 +23,7 @@ android:importantForAccessibility="yes" android:background="@drawable/keyboard_quick_switch_task_view_background" android:clipToOutline="true" - launcher:borderColor="?androidprv:attr/materialColorOutline"> + launcher:focusBorderColor="?androidprv:attr/materialColorOutline"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/content" diff --git a/quickstep/res/layout/split_instructions_view.xml b/quickstep/res/layout/split_instructions_view.xml index 91fb05cd0a..c663bf470c 100644 --- a/quickstep/res/layout/split_instructions_view.xml +++ b/quickstep/res/layout/split_instructions_view.xml @@ -24,12 +24,15 @@ android:paddingTop="@dimen/split_instructions_vertical_padding" android:paddingBottom="@dimen/split_instructions_vertical_padding" android:elevation="@dimen/split_instructions_elevation" - android:visibility="gone"> + android:visibility="gone" + android:importantForAccessibility="yes"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/split_instructions_text" android:layout_height="wrap_content" android:layout_width="wrap_content" android:gravity="center" android:textColor="?androidprv:attr/textColorOnAccent" + android:drawableEnd="@drawable/ic_split_exit" + android:drawablePadding="@dimen/split_instructions_drawable_padding" android:text="@string/toast_split_select_app" /> </com.android.quickstep.views.SplitInstructionsView>
\ No newline at end of file diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml index 4865aef5af..29c9992999 100644 --- a/quickstep/res/layout/task.xml +++ b/quickstep/res/layout/task.xml @@ -24,7 +24,8 @@ android:clipChildren="false" android:defaultFocusHighlightEnabled="false" android:focusable="true" - launcher:borderColor="?androidprv:attr/materialColorOutline"> + launcher:focusBorderColor="?androidprv:attr/materialColorOutline" + launcher:hoverBorderColor="?androidprv:attr/materialColorPrimary"> <com.android.quickstep.views.TaskThumbnailView android:id="@+id/snapshot" diff --git a/quickstep/res/layout/task_desktop.xml b/quickstep/res/layout/task_desktop.xml index fd82c66df5..06f4d0686f 100644 --- a/quickstep/res/layout/task_desktop.xml +++ b/quickstep/res/layout/task_desktop.xml @@ -25,7 +25,8 @@ android:clipToOutline="true" android:defaultFocusHighlightEnabled="false" android:focusable="true" - launcher:borderColor="?androidprv:attr/materialColorOutline"> + launcher:focusBorderColor="?androidprv:attr/materialColorOutline" + launcher:hoverBorderColor="?androidprv:attr/materialColorPrimary"> <View android:id="@+id/background" diff --git a/quickstep/res/layout/task_grouped.xml b/quickstep/res/layout/task_grouped.xml index c9fa9c07aa..75ff6268e4 100644 --- a/quickstep/res/layout/task_grouped.xml +++ b/quickstep/res/layout/task_grouped.xml @@ -29,7 +29,8 @@ android:clipChildren="false" android:defaultFocusHighlightEnabled="false" android:focusable="true" - launcher:borderColor="?androidprv:attr/materialColorOutline"> + launcher:focusBorderColor="?androidprv:attr/materialColorOutline" + launcher:hoverBorderColor="?androidprv:attr/materialColorPrimary"> <com.android.quickstep.views.TaskThumbnailView android:id="@+id/snapshot" diff --git a/quickstep/res/layout/taskbar_all_apps.xml b/quickstep/res/layout/taskbar_all_apps.xml index 976cd9e2a7..e234165d61 100644 --- a/quickstep/res/layout/taskbar_all_apps.xml +++ b/quickstep/res/layout/taskbar_all_apps.xml @@ -14,18 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. --> -<com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView +<com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:accessibilityPaneTitle="@string/all_apps_label"> - - <com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView - android:id="@+id/apps_view" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:clipChildren="true" - android:clipToPadding="false" - android:focusable="false" - android:saveEnabled="false" /> -</com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView> + android:clipChildren="true" + android:clipToPadding="false" + android:focusable="false" + android:saveEnabled="false" /> diff --git a/quickstep/res/layout/taskbar_all_apps_sheet.xml b/quickstep/res/layout/taskbar_all_apps_sheet.xml new file mode 100644 index 0000000000..a1d5fa6414 --- /dev/null +++ b/quickstep/res/layout/taskbar_all_apps_sheet.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:accessibilityPaneTitle="@string/all_apps_label"> + + <include + android:id="@+id/apps_view" + layout="@layout/taskbar_all_apps" /> +</com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView> diff --git a/quickstep/res/layout/taskbar_divider.xml b/quickstep/res/layout/taskbar_divider.xml index 73f3811ecf..0a92fa92db 100644 --- a/quickstep/res/layout/taskbar_divider.xml +++ b/quickstep/res/layout/taskbar_divider.xml @@ -13,16 +13,9 @@ See the License for the specific language governing permissions and limitations under the License. --> -<FrameLayout +<com.android.launcher3.views.IconButtonView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="@dimen/taskbar_icon_min_touch_size" android:layout_height="@dimen/taskbar_icon_min_touch_size" android:contentDescription="@string/taskbar_divider_a11y_title" - android:backgroundTint="@android:color/transparent"> - - <View - android:layout_height="32dp" - android:layout_width="2dp" - android:layout_gravity="center" - android:background="@drawable/taskbar_divider_bg" /> -</FrameLayout>
\ No newline at end of file + android:backgroundTint="@android:color/transparent" />
\ No newline at end of file diff --git a/quickstep/res/layout/taskbar_divider_popup_menu.xml b/quickstep/res/layout/taskbar_divider_popup_menu.xml index 195443ead9..00e47c9bed 100644 --- a/quickstep/res/layout/taskbar_divider_popup_menu.xml +++ b/quickstep/res/layout/taskbar_divider_popup_menu.xml @@ -32,7 +32,7 @@ android:clickable="true" android:gravity="center_vertical" android:orientation="horizontal" - android:background="@drawable/top_rounded_popup_ripple" + android:background="@drawable/rounded_popup_ripple" android:paddingEnd="10dp" android:paddingStart="10dp" android:theme="@style/PopupItem"> @@ -59,40 +59,4 @@ android:text="@string/always_show_taskbar" /> </LinearLayout> - - <LinearLayout - android:id="@+id/navigation_mode_switch_option" - android:layout_width="match_parent" - android:layout_height="52dp" - android:layout_gravity="center_vertical" - android:elevation="2dp" - android:clickable="true" - android:focusable="true" - android:background="@drawable/bottom_rounded_popup_ripple" - android:gravity="center_vertical" - android:orientation="horizontal" - android:paddingEnd="10dp" - android:paddingStart="10dp" - android:theme="@style/PopupItem"> - - <View - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_margin="4dp" - android:background="@drawable/ic_touch" - android:backgroundTint="?android:attr/textColorPrimary" /> - - <com.android.launcher3.BubbleTextView - style="@style/BaseIcon" - android:id="@+id/change_navigation_mode_text" - android:gravity="start|center_vertical" - android:textAlignment="viewStart" - android:paddingStart="12dp" - android:singleLine="true" - android:ellipsize="end" - android:textSize="14sp" - android:textColor="?android:attr/textColorPrimary" - android:text="@string/change_navigation_mode" /> - - </LinearLayout> </com.android.launcher3.taskbar.TaskbarDividerPopupView>
\ No newline at end of file diff --git a/quickstep/res/layout/transient_taskbar.xml b/quickstep/res/layout/transient_taskbar.xml index bf4b811bb3..0890a4e6b8 100644 --- a/quickstep/res/layout/transient_taskbar.xml +++ b/quickstep/res/layout/transient_taskbar.xml @@ -44,12 +44,12 @@ android:layout_height="@dimen/bubblebar_size" android:layout_gravity="bottom|end" android:layout_marginEnd="@dimen/transient_taskbar_bottom_margin" - android:layout_marginBottom="@dimen/transient_taskbar_bottom_margin" android:paddingEnd="@dimen/taskbar_icon_spacing" android:paddingStart="@dimen/taskbar_icon_spacing" android:visibility="gone" android:gravity="center" android:clipChildren="false" + android:elevation="@dimen/bubblebar_elevation" /> <FrameLayout diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml index f7a1d1a1f6..bca1e8c52a 100644 --- a/quickstep/res/values-af/strings.xml +++ b/quickstep/res/values-af/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Programvoorstelle is geaktiveer"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Programvoorstelle is gedeaktiveer"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Voorspelde program: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Draai jou toestel"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Draai asseblief jou toestel om die tutoriaal oor gebaarnavigasie te voltooi"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Maak seker dat jy van die rand heel regs of heel links af swiep"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Maak seker dat jy van die regter- of linkerrand af na die middel van die skerm toe swiep en laat los"</string> - <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Jy het geleer hoe om van regs af te swiep om terug te gaan. Nou kan jy leer hoe om tussen apps te wissel."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Jy het die Gaan Terug-gebaar voltooi"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Maak seker dat jy nie te naby aan die onderkant van die skerm swiep nie"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Maak seker dat jy van die rand heel regs of heel links af swiep."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Maak seker dat jy van die regter- of linkerrand na die middel van die skerm swiep en laat los."</string> + <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Jy het geleer hoe om van regs af te swiep om terug te gaan. Nou kan jy leer hoe om tussen programme te wissel."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Jy het die \"gaan terug\"-gebaar voltooi."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Maak seker dat jy nie te naby aan die onderkant van die skerm swiep nie."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Gaan na Instellings om sensitiwiteit van teruggebaar te verander"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Swiep om terug te gaan"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Swiep van die linker- of regterrand na die middel van die skerm om na die vorige skerm terug te gaan."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Swiep met 2 vingers van die linker- of regterkant van die skerm af na die middel toe om terug te keer na die vorige skerm."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Gaan terug"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Swiep van die linker- of regterrand af na die middel van die skerm toe"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Maak seker dat jy van die onderrand van die skerm af opswiep"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Maak seker jy hou nie stil voordat jy laat los nie"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Maak seker jy swiep reguit op"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Jy het die Gaan na Tuisskerm-gebaar voltooi. Nou kan jy leer hoe om terug te gaan."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Jy het die Gaan na Tuisskerm-gebaar voltooi"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Maak seker dat jy van die onderrand van die skerm af opswiep."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Maak seker jy onderbreek nie voordat jy laat los nie."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Maak seker jy swiep reguit op."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Jy het die \"gaan na tuisskerm\"-gebaar voltooi. Nou kan jy leer hoe om terug te gaan."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Jy het die \"gaan na tuisskerm\"-gebaar voltooi."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Swiep om na tuisskerm toe te gaan"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swiep op van die onderkant van jou skerm af. Hierdie gebaar neem jou altyd na die tuisskerm toe."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Swiep met 2 vingers op van die onderkant van die skerm af. Dié gebaar neem jou altyd na die tuisskerm toe."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Gaan na tuisskerm"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Swiep van die onderkant van jou skerm af op"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Knap gedaan!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Maak seker dat jy van die onderrand van die skerm af opswiep"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Probeer om die venster langer te hou voordat jy laat los"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Maak seker dat jy reguit opswiep en dan stilhou"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Swiep enige tyd van die onderkant van jou skerm af op om na jou tuisskerm toe te gaan"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Maak seker dat jy van die onderrand van die skerm af opswiep."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Probeer om die venster langer te hou voordat jy laat los."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Maak seker dat jy reguit opswiep en dan onderbreek."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Jy het geleer hoe om gebare te gebruik. Gaan na Instellings om gebare af te skakel."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Jy het die Wissel Apps-gebaar voltooi"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Jy het die \"wissel tussen programme\"-gebaar voltooi."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swiep om tussen programme te wissel"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Swiep van die onderkant van jou skerm af op, hou en laat los dan om tussen apps te wissel."</string> - <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Swiep met 2 vingers op van die onderkant van jou skerm af, hou en laat los dan om tussen apps te wissel."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Swiep van die onderkant van jou skerm af op, hou en laat los dan om tussen programme te wissel."</string> + <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Swiep met 2 vingers op van die onderkant van jou skerm af, hou en laat los dan om tussen programme te wissel."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Wissel apps"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Swiep van die onderkant van jou skerm af op, hou, en los dan"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Welgedaan!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Gereed"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Klaar"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Instellings"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taakbalk word gewys"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taakbalk is versteek"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigasiebalk"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Wys Taakbalk altyd"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Verander navigasiemodus"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taakbalkverdeler"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Skuif na links bo"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Skuif na regs onder"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Wys nog # app.}other{Wys nog # apps.}}"</string> diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml index 9b30d67d4a..a226a04359 100644 --- a/quickstep/res/values-am/strings.xml +++ b/quickstep/res/values-am/strings.xml @@ -33,52 +33,46 @@ <string name="all_apps_prediction_tip" msgid="2672336544844936186">"የእርስዎ የሚገመቱ መተግበሪያዎች"</string> <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"በመነሻ ገጽዎ ታችኛው ረድፍ ላይ የመተግበሪያ አስተያየት ጥቆማዎችን ያግኙ"</string> <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"በመነሻ ማያ ገጽዎ የተወዳጆች ረድፍ ላይ የመተግበሪያ አስተያየት ጥቆማዎችን ያግኙ"</string> - <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"በጣም ሥራ ላይ የዋሉ መተግበሪያዎችዎን በቀላሉ ከመነሻ ገጹ ሆነው ይድረሱባቸው። የአስተያየት ጥቆማዎች በእርስዎ ዕለት ተዕለት ተግባራት ላይ በመመስረት ይቀየራሉ። በታችኛው ረድፍ ላይ ያሉ መተግበሪያዎች ወደ መነሻ ገጽዎ ይወሰዳሉ።"</string> - <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"በጣም ሥራ ላይ የዋሉ መተግበሪያዎችዎን በቀላሉ ከመነሻ ገጹ ሆነው ይድረሱባቸው። የአስተያየት ጥቆማዎች በእርስዎ ዕለት ተዕለት ተግባራት ላይ በመመሥረት ይቀየራሉ። በተወዳጆች ረድፍ ውስጥ ያሉ መተግበሪያዎች ወደ የእርስዎ መነሻ ማያ ገፅ ይንቀሳቀሳሉ።"</string> + <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"በጣም ስራ ላይ የዋሉ መተግበሪያዎችዎን በቀላሉ ከመነሻ ገጹ ሆነው ይድረሱባቸው። የአስተያየት ጥቆማዎች በእርስዎ ዕለት ተዕለት ተግባራት ላይ በመመስረት ይቀየራሉ። በታችኛው ረድፍ ላይ ያሉ መተግበሪያዎች ወደ መነሻ ገጽዎ ይወሰዳሉ።"</string> + <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"በጣም ሥራ ላይ የዋሉ መተግበሪያዎችዎን በቀላሉ ከመነሻ ገጹ ሆነው ይድረሱባቸው። የአስተያየት ጥቆማዎች በእርስዎ ዕለት ተዕለት ተግባራት ላይ በመመሥረት ይቀየራሉ። በተወዳጆች ረድፍ ውስጥ ያሉ መተግበሪያዎች ወደ የእርስዎ መነሻ ማያ ገጽ ይንቀሳቀሳሉ።"</string> <string name="hotseat_edu_accept" msgid="1611544083278999837">"የመተግበሪያ አስተያየት ጥቆማዎችን አግኝ"</string> <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"አይ፣ አመሰግናለሁ"</string> <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ቅንብሮች"</string> - <string name="hotseat_auto_enrolled" msgid="522100018967146807">"በብዛት ሥራ ላይ የዋሉ መተግበሪያዎች እዚህ ይመጣሉ፣ እና በዕለት ተዕለት ተግባራት ላይ በመመስረት ይቀየራሉ"</string> + <string name="hotseat_auto_enrolled" msgid="522100018967146807">"በብዛት ስራ ላይ የዋሉ መተግበሪያዎች እዚህ ይመጣሉ፣ እና በዕለት ተዕለት ተግባራት ላይ በመመስረት ይቀየራሉ"</string> <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"የመተግበሪያ ጥቆማዎችን ለማግኘት መተግበሪያዎችን ከታችኛው ረድፍ ይጎትቱ"</string> <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"የመተግበሪያ አስተያየት ጥቆማዎች ወደ ባዶ ቦታ ታክለዋል"</string> <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"የመተግበሪያ አስተያየት ጥቆማዎች ነቅቷል"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"የመተግበሪያ አስተያየቶች ቦዝነዋል"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"የተገመተው መተግበሪያ፦ <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"መሣሪያዎን ያሽከርክሩ"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"የእጅ ምልክት ዳሰሳ አጋዥ ሥልጠናን ለማጠናቀቅ እባክዎ መሣሪያዎን ያሽከርክሩ"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ከቀኝ ጥግ ወይም ከግራ ጥግ ጠርዝ ጀምሮ ማንሸራተትዎን ያረጋግጡ"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"ከቀኝ ወይም ከግራ ጠርዝ ወደ ማያ ገጹ መሃል ማንሸራተትዎን እና መልቀቅዎን ያረጋግጡ"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"ከቀኝ ጠርዝ ወይም ከግራ ጠርዝ ጥግ ጀምሮ ማንሸራተትዎን ያረጋግጡ።"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"ከቀኝ ወይም ከግራ ጠርዝ ወደ ማያ ገጹ መሃል ማንሸራተትዎን እና መልቀቅዎን ያረጋግጡ።"</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"ወደ ኋላ ለመመለስ ከቀኝ ጀምሮ እንዴት ማንሸራተት እንደሚችሉ አውቀዋል። ቀጥለው መተግበሪያዎችን እንዴት መቀየር እንደሚችሉ ይወቁ።"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"ወደኋላ የመመለስ ምልክትን አጠናቅቀዋል"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"ከማያ ገጹ ታችኛው ክፍል ጋር በጣም ጠጋ ብለው አለማንሸራተትዎን ያረጋግጡ"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"ወደኋላ የመመለስ ምልክትን አጠናቀዋል።"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ከማያ ገጹ ታችኛው ክፍል ጋር በጣም ጠጋ ብለው አለማንሸራተትዎን ያረጋግጡ።"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ከኋላ ስሜት ሰጭነት ደረጃ ለመለወጥ ወደ ቅንብሮች ይመለሱ"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"ወደኋላ ለመመለስ ያንሸራትቱ"</string> - <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ወደ መጨረሻው ማያ ገፅ ለመመለስ ከግራ ወይም ከቀኝ ጠርዝ ወደ ማያ ገጹ መሃል ያንሸራትቱ።"</string> - <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"ወደ መጨረሻው ማያ ገፅ ለመመለስ በ2 ጣቶች ከግራ ወይም ከቀኝ ጠርዝ ወደ ማያ ገጹ መሃል ያንሸራትቱ።"</string> + <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ወደ መጨረሻው ማያ ገጽ ለመመለስ ከግራ ወይም ከቀኝ ጠርዝ ወደ ማያ ገጹ መሃል ያንሸራትቱ።"</string> + <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"ወደ መጨረሻው ማያ ገጽ ለመመለስ በ2 ጣቶች ከግራ ወይም ከቀኝ ጠርዝ ወደ ማያ ገጹ መሃል ያንሸራትቱ።"</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"ተመለስ"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"ከግራ ወይም ከቀኝ ጠርዝ ወደ ማያ ገጹ መካከል ያንሸራትቱ"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"ከማያ ገጹ የታችኛው ጠርዝ ወደ ላይ ማንሸራተትዎን ያረጋግጡ"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"ከመልቀቅዎ በፊት ባሉበት እንዳልቆሙ ያረጋግጡ"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"በቀጥታ ወደ ላይ ማንሸራተትዎን ያረጋግጡ"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"ወደ መነሻ ሂድ ምልክትን አጠናቅቀዋል። ቀጥሎ ደግሞ እንዴት ወደ ኋላ መመለስ እንደሚቻል ይማሩ።"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"ወደ መነሻ ሂድ ምልክትን አጠናቅቀዋል"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ከማያ ገጹ የታችኛው ጫፍ ወደ ላይ ማንሸራተትዎን ያረጋግጡ።"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ከመልቀቅዎ በፊት ለአፍታ እንዳልቆሙ ያረጋግጡ።"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"በቀጥታ ወደ ላይ ማንሸራተትዎን ያረጋግጡ።"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"የወደ መነሻ ሂድ ምልክትን አጠናቀዋል። ቀጥሎም ወደ ኋላ እንዴት መሄድ እንደሚችሉ ይወቁ።"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"የወደ መነሻ ሂድ ምልክትን አጠናቀዋል።"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"ወደ መነሻ ለመሄድ ያንሸራትቱ"</string> - <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ከእርስዎ ማያ ገፅ ግርጌ ላይ ወደ ላይ በጣት ጠረግ ያድርጉ። ይህ የእጅ ውዝዋዜ ሁልጊዜ ወደ መነሻ ማያ ገፅ ይወስድዎታል።"</string> - <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"በ2 ጣቶች ከማያ ገጹ ግርጌ ወደ ላይ ያንሸራትቱ። ይህ የእጅ ምልክት ሁልጊዜ ወደ መነሻ ማያ ገፅ ይወስደዎታል።"</string> + <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ከእርስዎ ማያ ገጽ ግርጌ ላይ ወደ ላይ በጣት ጠረግ ያድርጉ። ይህ የእጅ ውዝዋዜ ሁልጊዜ ወደ መነሻ ማያ ገጽ ይወስድዎታል።"</string> + <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"በ2 ጣቶች ከማያ ገጹ ግርጌ ወደ ላይ ያንሸራትቱ። ይህ የእጅ ምልክት ሁልጊዜ ወደ መነሻ ማያ ገጽ ይወስደዎታል።"</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ወደ መነሻ ይሂዱ"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"ከማያ ገጽዎ የታችኛው ክፍል ወደ ላይ ያንሸራትቱ"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"ጥሩ ሠርተዋል!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"ከማያ ገጹ የታችኛው ጠርዝ ወደ ላይ ማንሸራተትዎን ያረጋግጡ"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"ከመልቀቅዎ በፊት መስኮቱን ረዘም ላለ ጊዜ ለመያዝ ይሞክሩ"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"በቀጥታ ወደ ላይ ማንሸራተትዎን ያረጋግጡ፣ ከዚያ ባለበት ያቁሙ"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"በማንኛውም ጊዜ ወደ መነሻ ማያ ገጽዎ ለመሄድ ከማያ ገጽዎ የታችኛው ክፍል ወደ ላይ ያንሸራትቱ"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ከማያ ገጹ የታችኛው ጫፍ ወደ ላይ ማንሸራተትዎን ያረጋግጡ።"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"ከመልቀቅዎ በፊት መስኮቱን ረዘም ላለ ጊዜ ለመያዝ ይሞክሩ።"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"በቀጥታ ወደ ላይ ማንሸራተትዎን ያረጋግጡ፣ ከዚያ ለአፍታ ያቁሙ።"</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"የእጅ ምልክቶችን እንዴት እንደሚጠቀሙ ተምረዋል። የእጅ ምልክቶችን ለማጥፋት ወደ ቅንብሮች ይሂዱ።"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"መተግበሪያዎችን ቀይር ምልክትን አጠናቅቀዋል"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"መተግበሪያዎችን የመቀያየር ምልክትን አጠናቀዋል።"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"መተግበሪያዎችን ለመቀየር ያንሸራትቱ"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"በመተግበሪያዎች መካከል ለመቀያየር ከማያ ገጽዎ ግርጌ ወደ ላይ ያንሸራትቱ፣ ይያዙ፣ ከዚያ ይለቀቁ።"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"በመተግበሪያዎች መካከል ለመቀያየር ከማያ ገጽዎ ግርጌ ላይ በ2 ጣቶች ያንሸራትቱ፣ ይያዙ፣ ከዚያ ይለቀቁ።"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"መተግበሪያዎችን ይቀያይሩ"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"ከማያ ገጽዎ የታችኛው ክፍል ወደ ላይ ያንሸራትቱ፣ ይያዙ፣ ከዚያ ይልቀቁ"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"ጥሩ ሰርተዋል!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"ሁሉም ዝግጁ"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"ተጠናቋል"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ቅንብሮች"</string> @@ -92,7 +86,7 @@ <string name="default_device_name" msgid="6660656727127422487">"መሣሪያ"</string> <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"የስርዓት አሰሳ ቅንብሮች"</annotation></string> <string name="action_share" msgid="2648470652637092375">"አጋራ"</string> - <string name="action_screenshot" msgid="8171125848358142917">"ቅጽበታዊ ገፅ ዕይታ"</string> + <string name="action_screenshot" msgid="8171125848358142917">"ቅጽበታዊ ገጽ እይታ"</string> <string name="action_split" msgid="2098009717623550676">"ክፈል"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"የተከፈለ ማያ ገጽን ለመጠቀም ሌላ መተግበሪያ መታ ያድርጉ"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"የተከፈለ ማያ ገጽን ለመቀበል ሌላ መተግበሪያ ይምረጡ"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"የተግባር አሞሌ ይታያል"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"የተግባር አሞሌ ተደብቋል"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"የአሰሳ አሞሌ"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"ሁልጊዜ የተግባር አሞሌ ያሳዩ"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"የአሰሳ ሁነታን ይለውጡ"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"የተግባር አሞሌ አካፋይ"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ወደ ላይ/ግራ ይውሰዱ"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ወደ ታች/ቀኝ ይውሰዱ"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{ተጨማሪ # መተግበሪያ አሳይ።}one{ተጨማሪ # መተግበሪያ አሳይ።}other{ተጨማሪ # መተግበሪያዎች አሳይ።}}"</string> diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml index f58a75e07c..560f1c2c27 100644 --- a/quickstep/res/values-ar/strings.xml +++ b/quickstep/res/values-ar/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"تم تفعيل ميزة \"التطبيقات المقترحة\"."</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ميزة \"التطبيقات المقترحة\" غير مفعّلة."</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"التطبيق المتوقع: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"يُرجى تدوير الجهاز"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"يُرجى تدوير جهازك لإكمال الدليل التوجيهي للتنقُّل بالإيماءات."</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"تأكَّد من التمرير سريعًا من أقصى الحافة اليسرى أو اليمنى."</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"تأكَّد من التمرير سريعًا من الحافة اليسرى أو اليمنى إلى وسط الشاشة ثم ارفع إصبعك."</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"تأكّد من التمرير سريعًا من أقصى الحافة اليسرى أو اليمنى."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"تأكّد من التمرير سريعًا من الحافة اليسرى أو اليمنى إلى وسط الشاشة ثم ارفع إصبعك."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"لقد تعلمت كيفية التمرير سريعًا من اليسار للرجوع. تعرّف بعد ذلك على كيفية التبديل بين التطبيقات."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"لقد أكملت التدريب على إيماءة الرجوع."</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"تأكَّد من عدم التمرير سريعًا بالقرب من أسفل الشاشة."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"لقد أكملت التدريب على إيماءة الرجوع."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"تأكّد من عدم التمرير بالقرب من أسفل الشاشة."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"لتغيير مستوى حساسية إيماءة الرجوع، انتقِل إلى \"الإعدادات\""</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"مرِّر سريعًا للرجوع."</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"للرجوع إلى الشاشة السابقة، مرِّر سريعًا من الحافة اليسرى أو الحافة اليمنى إلى وسط الشاشة."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"للرجوع إلى الشاشة السابقة، عليك التمرير سريعًا بإصبعين من الحافة اليسرى أو اليمنى نحو وسط الشاشة."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"الرجوع"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"مرِّر سريعًا من الحافة اليمنى أو اليسرى إلى منتصف الشاشة."</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"تأكَّد من التمرير سريعًا من الحافة السفلية للشاشة إلى أعلاها."</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"تأكَّد من عدم التوقّف مؤقتًا قبل رفع إصبعك."</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"تأكَّد من التمرير للأعلى مباشرةً."</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"لقد أكملت التدريب على إيماءة الانتقال إلى الشاشة الرئيسية. تعرَّف بعد ذلك على كيفية الرجوع."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"لقد أكملت التدريب على إيماءة الانتقال إلى الشاشة الرئيسية."</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"تأكّد من التمرير سريعًا من الحافة السفلى للشاشة إلى أعلاها."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"تأكّد من عدم التوقّف قليلاً قبل رفع إصبعك."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"تأكّد من التمرير إلى الأعلى مباشرةً."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"لقد أكملت التدريب على إيماءة الانتقال إلى الشاشة الرئيسية. تعرّف بعد ذلك على كيفية الرجوع."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"أكملت التدريب على إيماءة الانتقال إلى الشاشة الرئيسية."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"مرِّر سريعًا للانتقال إلى الشاشة الرئيسية"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"مرِّر سريعًا من أسفل الشاشة إلى أعلاها. تنقلك هذه الإيماءة دائمًا إلى الشاشة الرئيسية."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"مرِّر سريعًا بإصبعين من أسفل الشاشة إلى أعلاها. تنقلك هذه الإيماءة دائمًا إلى الشاشة الرئيسية."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"الانتقال إلى الشاشة الرئيسية"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"مرِّر سريعًا من أسفل الشاشة إلى أعلاها."</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"أحسنت صنعًا."</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"تأكَّد من التمرير سريعًا من الحافة السفلية للشاشة إلى أعلاها."</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"حاوِل إبقاء إصبعك على النافذة لمدة أطول قبل رفعه."</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"تأكَّد من التمرير سريعًا للأعلى مباشرةً ثم التوقّف مؤقتًا."</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"للانتقال إلى شاشتك الرئيسية في أي وقت، اسحب لأعلى الشاشة من أسفلها."</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"تأكّد من التمرير سريعًا من الحافة السفلى للشاشة إلى أعلاها."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"حاوِل إبقاء إصبعك على النافذة لمدة أطول قبل رفعه."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"تأكّد من التمرير سريعًا للأعلى مباشرةً ثم التوقّف قليلاً."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"لقد تعرّفت على كيفية استخدام الإيماءات. لإيقاف الإيماءات، انتقِل إلى \"الإعدادات\"."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"لقد أكملت التدريب على إيماءة التبديل بين التطبيقات."</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"أكملت التدريب على إيماءة التبديل بين التطبيقات."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"التمرير سريعًا للتبديل بين التطبيقات"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"للتبديل بين التطبيقات، مرِّر سريعًا من أسفل الشاشة إلى الأعلى ثمّ ثبِّت إصبعك قليلاً قبل رفعه."</string> - <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"للتبديل بين التطبيقات، مرِّر سريعًا من أسفل الشاشة إلى أعلاها بإصبعين، ثمّ ثبّتهما قليلاً قبل رفعهما."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"للتبديل بين التطبيقات، مرِّر سريعًا من أسفل الشاشة لأعلاها مع تثبيت إصبعك ثم ارفعه."</string> + <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"للتبديل بين التطبيقات، مرِّر سريعًا من أسفل الشاشة إلى أعلاها بإصبعين مع تثبيتهما، ثم ارفعهما."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"تبديل التطبيقات"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"مرِّر سريعًا من أسفل الشاشة إلى أعلاها، وأبقِ إصبعك على الشاشة قليلاً ثم ارفعه."</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"أحسنت."</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"اكتمل التدريب على الإيماءة"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"تم"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"الإعدادات"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"تم إظهار شريط التطبيقات"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"تم إخفاء شريط التطبيقات"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"شريط التنقل"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"إظهار شريط التطبيقات دائمًا"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"تغيير وضع التنقل"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"مقسِّم شريط التطبيقات"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"الانتقال إلى يمين الشاشة أو أعلاها"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"الانتقال إلى يسار الشاشة أو أسفلها"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{إظهار تطبيق واحد آخر}zero{إظهار # تطبيق آخر}two{إظهار تطبيقَين آخرَين}few{إظهار # تطبيقات أخرى}many{إظهار # تطبيقًا آخر}other{إظهار # تطبيق آخر}}"</string> diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml index 05d1b996de..825a396f33 100644 --- a/quickstep/res/values-as/strings.xml +++ b/quickstep/res/values-as/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"এপৰ পৰামৰ্শসমূহ সক্ষম কৰা আছে"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"এপৰ পৰামৰ্শসমূহ অক্ষম কৰা আছে"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"পূৰ্বানুমান কৰা এপ্: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"আপোনাৰ ডিভাইচটো ঘূৰাওক"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"আঙুলিৰ স্পৰ্শৰ নিৰ্দেশেৰে কৰা নেভিগেশ্বনৰ টিউট’ৰিয়েল শেষ কৰিবলৈ অনুগ্ৰহ কৰি আপোনাৰ ডিভাইচটো ঘূৰাওক"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"আপুনি সোঁ অথবা বাওঁ কাষৰ একেবাৰে সীমাৰ পৰা ছোৱাইপ কৰাটো নিশ্চিত কৰক"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"আপুনি স্ক্ৰীনৰ সোঁ অথবা বাওঁ কাষৰ পৰা মধ্যভাগলৈকে ছোৱাইপ কৰি এৰি দিয়াটো নিশ্চিত কৰক"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"আপুনি সোঁ অথবা বাওঁ প্ৰান্তৰৰ একেবাৰে সীমাৰ পৰা ছোৱাইপ কৰাটো নিশ্চিত কৰক।"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"আপুনি স্ক্ৰীনৰ সোঁ অথবা বাওঁ প্ৰান্তৰৰ পৰা মধ্যভাগলৈকে ছোৱাইপ কৰি এৰি দিয়াটো নিশ্চিত কৰক।"</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"সোঁফালৰ পৰা ছোৱাইপ কৰি কেনেকৈ উভতি যাব লাগে, সেইটো আপুনি জানিলে। ইয়াৰ পাছত, এপ্ কেনেকৈ সলনি কৰিব সেয়া জানক।"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"আপুনি উভতি যাওক নিৰ্দেশটো সম্পূৰ্ণ কৰিলে"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"আপুনি স্ক্ৰীনৰ তলৰ অংশৰ বেছি ওচৰলৈ ছোৱাইপ নকৰাটো নিশ্চিত কৰক"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"আপুনি উভতি যাওক নিৰ্দেশটো সম্পূৰ্ণ কৰিলে।"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"আপুনি স্ক্ৰীনৰ তলৰ অংশৰ বেছি ওচৰলৈ ছোৱাইপ নকৰাটো নিশ্চিত কৰক।"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"উভতি যোৱাৰ নির্দেশটোৰ সংবেদনশীলতা সলনি কৰিবলৈ ছেটিঙলৈ যাওক"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"উভতি যাবলৈ ছোৱাইপ কৰক"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"অন্তিম স্ক্ৰীনখনলৈ উভতি যাবলৈ বাওঁ অথবা সোঁ প্ৰান্তৰৰ পৰা মাজলৈ ছোৱাইপ কৰক।"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"অন্তিম স্ক্ৰীনখনলৈ উভতি যাবলৈ ২ টা আঙুলিৰে স্ক্ৰীনখনৰ বাওঁ অথবা সোঁ প্ৰান্তৰৰ পৰা মাজলৈ ছোৱাইপ কৰক।"</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"উভতি যাওক"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"স্ক্ৰীনখনৰ বাওঁ অথবা সোঁ প্ৰান্তৰৰ পৰা মধ্যভাগলৈ ছোৱাইপ কৰক"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"আপুনি স্ক্ৰীনৰ তলৰ প্ৰান্তৰ পৰা ওপৰলৈ ছোৱাইপ কৰাটো নিশ্চিত কৰক"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"আপুনি এৰি দিয়াৰ পূৰ্বে অলপো নোৰোৱাটো নিশ্চিত কৰক"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"আপুনি পোনকৈ ওপৰলৈ ছোৱাইপ কৰাটো নিশ্চিত কৰক"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"আপুনি গৃহ স্ক্ৰীনলৈ যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে। ইয়াৰ পাছত, কেনেকৈ উভতি যাব সেয়া জানক।"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"আপুনি গৃহ স্ক্ৰীনলৈ যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"আপুনি স্ক্ৰীনৰ তলৰ প্ৰান্তৰ পৰা ওপৰলৈ ছোৱাইপ কৰাটো নিশ্চিত কৰক।"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"আপুনি এৰি দিয়াৰ পূৰ্বে অলপো নোৰোৱাটো নিশ্চিত কৰক।"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"আপুনি পোনকৈ ওপৰলৈ ছোৱাইপ কৰাটো নিশ্চিত কৰক।"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"আপুনি গৃহ স্ক্ৰীনলৈ যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে। ইয়াৰ পাছত, গৃহ স্ক্ৰীনলৈ কেনেকৈ যাব সেয়া জানক।"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"আপুনি গৃহ স্ক্ৰীনলৈ যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে।"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"গৃহ স্ক্ৰীনলৈ যাবলৈ ছোৱাইপ কৰক"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"আপোনাৰ স্ক্ৰীনৰ তলৰ অংশৰ পৰা ওপৰলৈ ছোৱাইপ কৰক। এই নিৰ্দেশটোৱে আপোনাক সদায় গৃহ স্ক্ৰীনলৈ লৈ যায়।"</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"স্ক্ৰীনখনৰ একেবাৰে তলৰ পৰা ২ টা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰক। এই নিৰ্দেশটোৱে আপোনাক সদায় গৃহ স্ক্ৰীনলৈ লৈ যায়।"</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"গৃহ পৃষ্ঠালৈ যাওক"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"আপোনাৰ স্ক্ৰীনৰ একেবাৰে তলৰ অংশৰ পৰা ওপৰলৈ ছোৱাইপ কৰক"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"বঢ়িয়া!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"আপুনি স্ক্ৰীনৰ তলৰ প্ৰান্তৰ পৰা ওপৰলৈ ছোৱাইপ কৰাটো নিশ্চিত কৰক"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"এৰি দিয়াৰ পূৰ্বে ৱিণ্ডখন দীৰ্ঘ সময়ৰ বাবে ধৰি ৰাখিবলৈ চেষ্টা কৰক"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"আপুনি স্ক্ৰীনৰ ওপৰলৈ পোনকৈ ছোৱাইপ কৰি তাৰ পাছত ৰোৱাটো নিশ্চিত কৰক"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"আপোনাৰ গৃহ স্ক্ৰীনলৈ যিকোনো সময়তে যাবলৈ, আপোনাৰ স্ক্ৰীনখনৰ একেবাৰে তলৰ পৰা ওপৰলৈ ছোৱাইপ কৰক"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"আপুনি স্ক্ৰীনৰ তলৰ প্ৰান্তৰ পৰা ওপৰলৈ ছোৱাইপ কৰাটো নিশ্চিত কৰক।"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"এৰি দিয়াৰ পূৰ্বে ৱিণ্ডখন দীৰ্ঘ সময়ৰ বাবে ধৰি ৰাখিবলৈ চেষ্টা কৰক।"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"আপুনি স্ক্ৰীনৰ ওপৰলৈ পোনকৈ ছোৱাইপ কৰি তাৰ পাছত ৰোৱাটো নিশ্চিত কৰক।"</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"আপুনি নিৰ্দেশসমূহ কেনেকৈ ব্যৱহাৰ কৰিব লাগে সেয়া জানিলে। নিৰ্দেশসমূহ অফ কৰিবলৈ, ছেটিঙলৈ যাওক।"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"আপুনি এপ্ সলনি কৰাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"আপুনি এপ্ সলনি কৰাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে।"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"এপ্ সলনি কৰিবলৈ ছোৱাইপ কৰক"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"এপ্সমূহ সালসলনিকৈ ব্যৱহাৰ কৰিবলৈ আপোনাৰ স্ক্ৰীনৰ একেবাৰে তলৰ অংশৰ পৰা ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক আৰু তাৰ পাছত এৰি দিয়ক।"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"এপ্সমূহ সালসলনিকৈ ব্যৱহাৰ কৰিবলৈ ২ টা আঙুলিৰে আপোনাৰ স্ক্ৰীনৰ একেবাৰে তলৰ অংশৰ পৰা ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক আৰু তাৰ পাছত এৰি দিয়ক।"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"এপ্ সলনি কৰক"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"আপোনাৰ স্ক্ৰীনৰ তলৰ অংশৰ পৰা ওপৰলৈ ছোৱাইপ কৰক, ধৰি ৰাখক, তাৰ পাছত এৰি দিয়ক"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"বঢ়িয়া!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"সম্পূৰ্ণ সাজু"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"হ’ল"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ছেটিং"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"টাস্কবাৰ দেখুওৱা হৈছে"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"টাস্কবাৰ লুকুৱাই থোৱা হৈছে"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"নেভিগেশ্বনৰ দণ্ড"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"টাস্কবাৰ সদায় দেখুৱাওক"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"নেভিগেশ্বন ম’ড সলনি কৰক"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"টাস্কবাৰ বিভাজক"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ওপৰৰ বাঁওফাললৈ নিয়ক"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"তলৰ সোঁফাললৈ নিয়ক"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{আৰু # টা এপ্ দেখুৱাওক।}one{আৰু # টা এপ্ দেখুৱাওক।}other{আৰু # টা এপ্ দেখুৱাওক।}}"</string> diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml index 3e5bb6fc3b..096299120a 100644 --- a/quickstep/res/values-az/strings.xml +++ b/quickstep/res/values-az/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Tətbiq təklifləri aktivdir"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Tətbiq təklifləri deaktivdir"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Proqnozlaşdırılan tətbiq: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Cihazı fırladın"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Jest naviqasiyası təlimatını tamamlamaq üçün cihazı fırladın"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Ən sağ və ya sol kənardan sürüşdürün"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Sağ və ya sol kənardan ekranın ortasına sürüşdürüb, buraxın"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Ekranın ən sağ və ya sol kənarından sürüşdürün."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Ekranın sağ və ya sol kənarından ortasına sürüşdürüb buraxın."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Geri qayıtmaq üçün sağdan sürüşdürmək qaydasını öyrəndiniz. Sonra tətbiqləri keçirməyi öyrənin."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Geri qayıtma jestini tamamladınız"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Barmağınızı ekranın aşağı kənarına çox yaxınlaşdırmayın"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Geri getmə jestini tamamladınız."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Ekranın altına çox yaxın sürüşdürmədiyinizə əmin olun."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Geri qayıtma jestinin həssaslığını dəyişmək üçün Ayarlara keçin"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Geri qayıtmaq üçün sürüşdürün"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Sonuncu ekrana qayıtmaq üçün ekranın sol, yaxud sağ kənarından mərkəzinə doğru sürüşdürün."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Sonuncu ekrana qayıtmaq üçün 2 barmaqla ekranın sol, yaxud sağ kənarından mərkəzinə doğru sürüşdürün."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Geri qayıdın"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Sol və ya sağ kənardan ekranın ortasına sürüşdürün"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Ekranın aşağı kənarından yuxarı sürüşdürün"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Buraxmazdan əvvəl durdurmayın"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Düz yuxarı sürüşdürün"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Əsas ekrana keçid jestini tamamladınız. Geri qayıtmağı öyrənin."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Əsas ekrana keçid jestini tamamladınız"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Ekranın ən kənar aşağısından yuxarı sürüşdürün."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Buraxmazdan əvvəl durdurmadığınıza əmin olun."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Birbaşa yuxarı sürüşdürün."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Əsas səhifəyə keçmə jestini tamamladınız. Sonra geri qayıtmağı öyrənin."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Əsas səhifəyə keçmə jestini tamamladınız."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Əsas səhifəyə keçmək üçün sürüşdürün"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Ekranın aşağısından yuxarısına sürüşdürün. Bu jest həmişə Əsas səhifəyə aparır."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"2 barmaqla ekranın aşağısından yuxarısına sürüşdürün. Bu jest həmişə Əsas səhifəyə aparır."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Əsas səhifəyə qayıdın"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Ekranın aşağısından yuxarı sürüşdürün"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Əla!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Ekranın aşağı kənarından yuxarı sürüşdürün"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Buraxmazdan öncə pəncərəni bir müddət saxlayın"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Düz yuxarı sürüşdürüb, durdurun"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"İstənilən vaxt əsas ekranınıza getmək üçün ekranın aşağısından yuxarı sürüşdürün"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Ekranın ən kənar aşağısından yuxarı sürüşdürün."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Barmağı buraxmadan öncə displeydə bir müddət saxlayın."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Sürüşdürüb ekranın yuxarı kənarında saxlayın."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Jestlərdən istifadə qaydasını öyrəndiniz. Jestləri deaktiv etmək üçün Ayarlara keçin."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Tətbiqlər arasında keçid jestini tamamladınız"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Tətbiqləri keçirmə jestini tamamladınız."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Tətbiqi keçirmək üçün sürüşdürün"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Tətbiqlər arasında keçid üçün ekranın aşağısından yuxarı doğru sürüşdürüb saxlayın, sonra buraxın."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Tətbiqlər arasında keçid üçün 2 barmaqla ekranın aşağısından yuxarı doğru sürüşdürüb saxlayın, sonra buraxın."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Tətbiqləri dəyişin"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Ekranın aşağısından yuxarı sürüşdürüb saxlayın, sonra buraxın"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Afərin!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Tam hazır"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Oldu"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Ayarlar"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"İşləmə paneli göstərilir"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"İşləmə paneli gizlədilib"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Naviqasiya paneli"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"İşləmə paneli həmişə görünsün"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Naviqasiya rejimini dəyişin"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"İşləmə paneli ayırıcısı"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Yuxarı/sola köçürün"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Aşağı/sağa köçürün"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Daha # tətbiqi göstərin.}other{Daha # tətbiqi göstərin.}}"</string> diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml index e6d87f8b5b..23dd072c97 100644 --- a/quickstep/res/values-b+sr+Latn/strings.xml +++ b/quickstep/res/values-b+sr+Latn/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Predlozi aplikacija su omogućeni"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Predlozi aplikacija su onemogućeni"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predviđamo aplikaciju: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotirajte uređaj"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rotirajte uređaj da biste dovršili vodič za navigaciju pomoću pokreta"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Obavezno prevucite od same desne ili leve ivice"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Obavezno prevucite od desne ili leve ivice do sredine ekrana i otpustite"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Obavezno prevucite od same desne ili leve ivice."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Obavezno prevucite od desne ili leve ivice do sredine ekrana i otpustite."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Naučili ste kako da prevlačite zdesna da biste se vratili unazad. Sada naučite da zamenite aplikacije."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Dovršili ste pokret za povratak"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Nikako ne prevlačite previše blizu dna ekrana"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Dovršili ste pokret za povratak."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Nikako ne prevlačite previše blizu dna ekrana."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Osetljivost pok. za nazad možete da promenite u Podešavanjima"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Prevucite da biste se vratili unazad"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Da biste se vratili na poslednji ekran, prevucite od leve ili desne ivice do sredine ekrana."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Da biste se vratili na poslednji ekran, prevucite pomoću dva prsta od leve ili desne ivice do sredine ekrana."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Nazad"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Prevucite od leve ili desne ivice do sredine ekrana"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Obavezno prevucite nagore od donje ivice ekrana"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Nikako ne stajte pre otpuštanja"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Obavezno prevucite pravo nagore"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Dovršili ste pokret za povratak na početnu stranicu. Sada saznajte kako da se vratite."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Dovršili ste pokret za povratak na početnu stranicu."</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Obavezno prevucite nagore od donje ivice ekrana."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Nikako ne stajte pre otpuštanja."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Obavezno prevucite pravo nagore."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Dovršili ste pokret za povratak na početnu stranicu. Sada saznajte kako da se vratite."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Dovršili ste pokret za povratak na početnu stranicu."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Prevucite da biste otišli na početnu stranicu"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Prevucite nagore od dna ekrana. Ovaj pokret vas uvek vodi na početni ekran."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Prevucite pomoću dva prsta nagore od dna ekrana. Ovim pokretom uvek otvarate početni ekran."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Idite na početni ekran"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Prevucite nagore sa donjeg dela ekrana"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Odlično!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Obavezno prevucite nagore od donje ivice ekrana"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Probajte da držite prozor duže pre otpuštanja"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Obavezno prevucite pravo nagore, pa zastanite"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Da biste otišli na početni ekran u bilo kom trenutku, prevucite nagore od dna ekrana."</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Obavezno prevucite nagore od donje ivice ekrana."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Probajte da držite prozor duže pre otpuštanja."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Obavezno prevucite pravo nagore, pa zastanite."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Naučili ste kako da koristite pokrete. Da biste isključili pokrete, idite na podešavanja."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Dovršili ste pokret za promenu aplikacija"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Dovršili ste pokret za promenu aplikacija."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Prevucite da biste zamenili aplikacije"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Za prelazak sa jedne aplikacije na drugu prevucite nagore od dna ekrana, zadržite, pa pustite."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Za prelazak između aplikacija prevucite pomoću dva prsta nagore od dna ekrana, zadržite, pa pustite."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Pređite na drugu aplikaciju"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Prevucite nagore od dna ekrana, zadržite, pa pustite"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Odlično!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"To je to"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Gotovo"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Podešavanja"</string> @@ -104,7 +98,7 @@ <string name="accessibility_rotate_button" msgid="4771825231336502943">"Rotirajte ekran"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Uputstva na traci zadataka"</string> <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Prevucite na stranu da biste koristili 2 aplikacije odjednom"</string> - <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Sporo prevucite nagore da biste prikazali traku zadataka"</string> + <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Nakratko prevucite nagore da biste prikazali traku zadataka"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Dobijajte predloge aplikacija na osnovu rutine"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Uključite navigaciju pomoću pokreta u Podešavanjima radi automatskog skrivanja trake zadataka"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Uradite više pomoću trake zadataka"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Traka zadataka je prikazana"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Traka zadataka je skrivena"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Traka za navigaciju"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Uvek prikazuj traku zadataka"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Promeni režim navigacije"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Razdelnik trake zadataka"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premesti gore levo"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premesti dole desno"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Prikaži još # aplikaciju.}one{Prikaži još # aplikaciju.}few{Prikaži još # aplikacije.}other{Prikaži još # aplikacija.}}"</string> diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml index c7358ffd7a..a5c218b9aa 100644 --- a/quickstep/res/values-be/strings.xml +++ b/quickstep/res/values-be/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Прапановы праграм уключаны"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Прапановы праграм выключаны"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Праграма з падказкі: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Павярніце прыладу"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Каб пачаць азнаямленне з дапаможнікам па навігацыі жэстамі, павярніце прыладу"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Правядзіце пальцам справа налева ці злева направа ад самага краю экрана"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Правядзіце пальцам ад правага або левага краю экрана ў цэнтр і адпусціце палец"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Пераканайцеся, што вы праводзіце пальцам ад самага вугла (правага ці левага) экрана."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Пераканайцеся, што праводзіце пальцам з правага ці левага вугла ў цэнтр экрана, а потым адпускаеце."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Вы даведаліся, як гартаць справа для вяртання. Цяпер даведайцеся, як пераключацца паміж праграмамі."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Вы навучыліся рабіць жэст для пераходу назад"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Не праводзьце пальцам занадта блізка да ніжняга краю экрана"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Вы навучыліся рабіць жэст вяртання."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Пераканайцеся, што вы не праводзіце пальцам занадта блізка да ніжняга краю экрана."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Каб змяніць адчувальнасць жэста вяртання, адкрыйце налады"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Правядзіце пальцам, каб вярнуцца"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Каб вярнуцца на папярэдні экран, правядзіце пальцам ад левага ці правага краю да цэнтра экрана."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Каб вярнуцца на папярэдні экран, правядзіце двума пальцамі ад левага ці правага краю ў цэнтр экрана."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Назад"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Правядзіце пальцам па экране злева направа ці справа налева ў цэнтр экрана"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Правядзіце пальцам знізу ўверх з ніжняга краю экрана"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Перш чым адпусціць палец, не затрымлівайце яго на адным месцы"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Правядзіце пальцам па экране роўна ўверх"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Вы навучыліся рабіць жэст для пераходу на галоўны экран. Зараз вы даведаецеся, як пераходзіць назад."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Вы навучыліся рабіць жэст для пераходу на галоўны экран"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Пераканайцеся, што праводзіце пальцам па экране знізу ўверх."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Пераканайцеся, што не затрымліваецеся перад адпусканнем."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Пераканайцеся, што праводзіце пальцам вертыкальна."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Вы навучыліся рабіць жэст пераходу на галоўны экран. А зараз даведайцеся, як вярнуцца назад."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Вы навучыліся рабіць жэст пераходу на галоўны экран."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Правядзіце пальцам для пераходу на галоўны экран"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Правядзіце пальцам па экране знізу ўверх. Гэты жэст дазваляе вярнуцца на Галоўны экран."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Правядзіце двума пальцамі па экране знізу ўверх. Гэты жэст дазваляе вярнуцца на Галоўны экран."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Пераход на галоўны экран"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Правядзіце пальцам па экране знізу ўверх"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"У вас добра атрымліваецца!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Правядзіце пальцам знізу ўверх з ніжняга краю экрана"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Перш чым адпусціць палец, паспрабуйце даўжэй утрымліваць акно націснутым"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Правядзіце пальцам па экране роўна ўверх, а потым затрымайце палец"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Перайсці на галоўны экран"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Каб у любы час адкрыць галоўны экран, правядзіце пальцам па экране знізу ўверх"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Пераканайцеся, што праводзіце пальцам па экране знізу ўверх."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Перш чым адпусціць палец, паспрабуйце даўжэй утрымліваць акно націснутым."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Пераканайцеся, што праводзіце пальцам вертыкальна, а потым затрымліваеце яго."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Вы навучыліся выкарыстоўваць жэсты. Каб выключыць жэсты, адкрыйце Налады."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Вы навучыліся рабіць жэст для пераключэння паміж праграмамі"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Вы навучыліся рабіць жэст пераключэння паміж праграмамі."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Правядзіце пальцам для пераключэння паміж праграмамі"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Каб пераключыцца на іншую праграму, правядзіце па экране знізу ўверх, патрымайце палец і адпусціце."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Каб пераключыцца на іншую праграму, правядзіце двума пальцамі знізу ўверх, патрымайце і адпусціце."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Пераключэнне праграм"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Правядзіце пальцам уверх ад ніжняга краю экрана, затрымайце палец, а потым адпусціце"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Выдатна!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Гатова"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Гатова"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Налады"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Панэль задач паказана"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Панэль задач схавана"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Панэль навігацыі"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Заўсёды паказваць панэль задач"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Змяніць рэжым навігацыі"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Раздзяляльнік панэлі задач"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перамясціць уверх/улева"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перамясціць уніз/управа"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Паказаць ячшэ # праграму.}one{Паказаць ячшэ # праграму.}few{Паказаць ячшэ # праграмы.}many{Паказаць ячшэ # праграм.}other{Паказаць ячшэ # праграмы.}}"</string> diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml index dbb878d04c..1222afa385 100644 --- a/quickstep/res/values-bg/strings.xml +++ b/quickstep/res/values-bg/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Предложенията за приложения са активирани"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Функцията „Предложения за приложения“ е деактивирана"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Предвидено приложение: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Завъртете устройството си"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Моля, завъртете устройството си, за да завършите урока за навигиране с жестове"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Трябва да плъзнете пръст от най-дясната или най-лявата част на екрана"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Трябва да плъзнете пръст от десния или левия край до средата на екрана, след което да го отпуснете"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Трябва да прекарате пръст от най-дясната или най-лявата част на екрана."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Трябва да прекарате пръст от десния или левия край на екрана до средата, след което да освободите."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Научихте жеста за връщане с плъзгане от дясно. Сега научете как се превключва между приложения."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Изпълнихте жеста за връщане назад"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Не плъзвайте пръста си твърде близо до долната част на екрана"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Изпълнихте жеста за връщане назад."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Не прекарвайте пръст твърде близо до долната част на екрана."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Променете чувств. на жеста за връщане назад от настройките"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Жест за връщане назад"</string> - <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"За да се върнете на предишния екран, плъзнете пръст от левия или десния край на екрана до средата."</string> + <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"За да се върнете на предишния екран, прекарайте пръст от левия или десния край на екрана до средата."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"За да се върнете към последния екран, прекарайте два пръста от левия или десния край на екрана до средата."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Връщане назад"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Плъзнете пръст от левия или десния край до средата на екрана"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Трябва да плъзнете пръст нагоре от долния край на екрана"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Не задържайте, преди да вдигнете пръста си"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Трябва да плъзнете пръст право нагоре"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Изпълнихте жеста за преминаване към началния екран. В следващия урок ще научите как се връща назад."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Изпълнихте жеста за преминаване към началния екран"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Трябва да прекарате пръст нагоре от долния край на екрана."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Не задържайте, преди да вдигнете пръста си."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Трябва да прекарате пръст право нагоре."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Изпълнихте жеста за преминаване към началния екран. В следващия урок ще научите как да се върнете назад."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Изпълнихте жеста за преминаване към началния екран."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Жест за преминаване към началния екран"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Прекарайте пръст нагоре от долната част на екрана. Този жест винаги ще ви отвежда до началния екран."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Прекарайте два пръста нагоре от долната част на екрана. Този жест винаги води до началния екран."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Отваряне на началния екран"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Плъзнете пръст нагоре от долната част на екрана"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Отлично!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Трябва да плъзнете пръст нагоре от долния край на екрана"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Задръжте прозореца по-дълго, преди да вдигнете пръста си"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Плъзнете пръст право нагоре, след което задръжте"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"За да отворите началния си екран по всяко време, прекарайте пръст нагоре от долната част на екрана"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Трябва да прекарате пръст нагоре от долния край на екрана."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Задръжте прозореца по-дълго, преди да вдигнете пръста си."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Прекарайте пръст право нагоре, след което задръжте."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Научихте как да използвате жестовете. За да ги изключите, отворете настройките."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Изпълнихте жеста за превключване между приложения"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Изпълнихте жеста за превключване между приложения."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Жест за превключване между приложенията"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"За превключване плъзнете пръст нагоре от долната част на екрана, задръжте и освободете."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"За превключване прекарайте пръст нагоре от долната част на екрана, задръжте и освободете"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"За превключване на прил. плъзнете 2 пръста нагоре от долната част на екрана, задръжте и освободете."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Превключване на приложенията"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Плъзнете пръст нагоре от долната част на екрана, задръжте и след това го вдигнете"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Браво!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Готово"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Готово"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Настройки"</string> @@ -86,7 +80,7 @@ <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Чудесно!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"Урок <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"Готово!"</string> - <string name="allset_hint" msgid="459504134589971527">"Плъзнете пръст нагоре, за да отворите началния екран"</string> + <string name="allset_hint" msgid="459504134589971527">"Прекарайте пръст нагоре, за да отворите началния екран"</string> <string name="allset_button_hint" msgid="2395219947744706291">"Докоснете бутона „Начало“, за да преминете към началния екран"</string> <string name="allset_description_generic" msgid="5385500062202019855">"Можете да започнете да използвате <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="default_device_name" msgid="6660656727127422487">"устройството"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Лентата на задачите се показва"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Лентата на задачите е скрита"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Лента за навигация"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Лентата на задачите винаги да се показва"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Промяна на режима на навигация"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Разделител на лентата на задачите"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Преместване горе/вляво"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Преместване долу/вдясно"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Показване на още # приложение.}other{Показване на още # приложения.}}"</string> diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml index 7d8316cd39..8f9fbee864 100644 --- a/quickstep/res/values-bn/strings.xml +++ b/quickstep/res/values-bn/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"অ্যাপ সাজেশন চালু করা আছে"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"অ্যাপ সাজেশন বন্ধ করা আছে"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"আপনার প্রয়োজন হতে পারে এমন অ্যাপ: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"আপনার ডিভাইস ঘোরান"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"জেসচার নেভিগেশন টিউটোরিয়াল সম্পূর্ণ করতে আপনার ডিভাইসটি ঘোরান"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"স্ক্রিনের একেবারে ডান বা বাঁদিকের প্রান্ত থেকে সোয়াইপ করেছেন কিনা তা দেখে নিন"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"স্ক্রিনের ডান বা বাঁদিকের প্রান্ত থেকে মাঝখান পর্যন্ত সোয়াইপ করেছেন কিনা দেখে নিয়ে আঙুল তুলে নিন"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"স্ক্রিনের একেবারে ডান বা বাঁদিকের প্রান্ত থেকে সোয়াইপ করেছেন কিনা তা দেখে নিন।"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"স্ক্রিনের ডান বা বাঁদিকের প্রান্ত থেকে মাঝখান পর্যন্ত সোয়াইপ করে আঙুল তুলে নিয়েছেন কিনা তা দেখে নিন।"</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"ফিরে যেতে, কীভাবে ডানদিক থেকে সোয়াইপ করতে হয় তা আপনি শিখেছেন। এরপর, একটি অ্যাপ থেকে অন্য অ্যাপে কীভাবে যাবেন জেনে নিন।"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"আপনি জেনেছেন হাতের জেসচার ব্যবহার করে আগের স্ক্রিনে কীভাবে ফিরে যাওয়া যায়"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"স্ক্রিনের নিচের প্রান্তের খুব কাছে পর্যন্ত যাতে সোয়াইপ না করেন সেটি ভাল করে দেখে নিন"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"আপনি ফিরে যাওয়ার জেসচার সম্পর্কে জেনেছেন।"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"স্ক্রিনের নিচের প্রান্তের খুব কাছে পর্যন্ত যাতে সোয়াইপ না করেন সেটি ভাল করে দেখে নিন।"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ফিরে যাওয়ার জেসচারের সেন্সিটিভিটি পরিবর্তন করতে, সেটিংসে যান"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"ফিরে যেতে সোয়াইপ করুন"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"শেষের স্ক্রিনে ফিরে যেতে, ডান বা বাঁ প্রান্ত থেকে স্ক্রিনের মাঝখান পর্যন্ত সোয়াইপ করুন।"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"শেষ স্ক্রিনে ফিরতে, বাম বা ডান প্রান্ত থেকে স্ক্রিনের মাঝামাঝি পর্যন্ত ২টি আঙুল দিয়ে সোয়াইপ করুন।"</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"ফিরে যান"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"স্ক্রিনের বাঁদিক বা ডানদিক থেকে মাঝখানে সোয়াইপ করুন"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"স্ক্রিনের নিচের প্রান্ত থেকে আপনি সোয়াইপ করেছেন কিনা ভাল করে দেখে নিন"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"আঙুল তুলে নেওয়ার আগে আপনি যাতে পজ না করেন সেটি ভাল করে দেখে নিন"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"আপনি উপরের দিকে সোজাসুজি সোয়াইপ করেছেন কিনা ভাল করে দেখে নিন"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"আপনি জেনেছেন হাতের জেসচার ব্যবহার করে হোম স্ক্রিনে কীভাবে যাওয়া যায়। এরপর, ফিরে কীভাবে যাবেন তা জেনে নিন।"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"আপনি জেনেছেন হাতের জেসচার ব্যবহার করে হোম স্ক্রিনে কীভাবে যাওয়া যায়"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"স্ক্রিনের নিচের প্রান্ত থেকে আপনি সোয়াইপ করেছেন কিনা ভাল করে দেখে নিন।"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"আঙুল তুলে নেওয়ার আগে আপনি যাতে পজ না করেন সেটি ভাল করে দেখে নিন।"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"আপনি উপরের দিকে সোজাসুজি সোয়াইপ করেছেন কিনা ভাল করে দেখে নিন।"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"আপনি হোম স্ক্রিনে যাওয়ার জেসচার সম্পর্কে জেনেছেন। এরপর, ফিরে কীভাবে যাবেন তা জেনে নিন।"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"আপনি হোম স্ক্রিনে যাওয়ার জেসচার সম্পর্কে জেনেছেন।"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"হোম স্ক্রিনে যেতে সোয়াইপ করুন"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"স্ক্রিনের নিচের প্রান্ত থেকে উপরের দিকে সোয়াইপ করুন। এটি করলে, আপনি সবসময় হোম স্ক্রিনে যেতে পারবেন।"</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"স্ক্রিনের নিচ থেকে ২টি আঙুল দিয়ে উপরে সোয়াইপ করুন। এই জেসচার সবসময় আপনাকে হোম স্ক্রিনে নিয়ে যায়।"</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"হোম স্ক্রিনে যান"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"আপনার স্ক্রিনের একেবারে নিচ থেকে সোয়াইপ করুন"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"অসাধারণ!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"স্ক্রিনের নিচের প্রান্ত থেকে আপনি সোয়াইপ করেছেন কিনা ভাল করে দেখে নিন"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"চেষ্টা করুন যাতে আঙুল সরিয়ে নেওয়ার আগে উইন্ডো বেশ কিছুক্ষণ প্রেস করে রাখা যায়"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"আপনি উপরের দিকে সোজাসুজি সোয়াইপ করেছেন কিনা ভাল করে দেখে নিয়ে তারপর পজ করুন"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"হোমে যান"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"যেকোনও সময় আপনার হোম স্ক্রিনে যেতে, স্ক্রিনের নিচ থেকে উপরের দিকে সোয়াইপ করুন"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"স্ক্রিনের নিচের প্রান্ত থেকে আপনি সোয়াইপ করেছেন কিনা ভাল করে দেখে নিন।"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"চেষ্টা করুন যাতে আঙুল সরিয়ে নেওয়ার আগে উইন্ডো কিছুক্ষণ প্রেস করে রাখা যায়।"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"আপনি উপরের দিকে সোজাসুজি সোয়াইপ করেছেন কিনা ভাল করে দেখে নিয়ে তারপর পজ করুন।"</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"জেসচার কীভাবে ব্যবহার করতে হয় আপনি তা শিখে ফেলেছেন। জেসচার বন্ধ করতে, সেটিংসে যান।"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"আপনি জেনেছেন যে হাতের জেসচার ব্যবহার করে একটি অ্যাপ থেকে অন্য অ্যাপে কীভাবে যাওয়া যায়"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"আপনি একটি অ্যাপ থেকে অন্য অ্যাপে যাওয়ার জেসচার সম্পর্কে জেনেছেন।"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"একটি অ্যাপ থেকে অন্য অ্যাপে যেতে সোয়াইপ করুন"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"একটি অ্যাপ থেকে অন্যটিতে পাল্টাতে, স্ক্রিনের নিচ থেকে উপরে সোয়াইপ করে ধরে রাখুন, তারপরে ছেড়ে দিন।"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"একটি থেকে অন্য অ্যাপে পাল্টাতে, ২টি আঙুল দিয়ে স্ক্রিনের নিচ থেকে উপরে সোয়াইপ করে ধরে রেখে ছেড়ে দিন।"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"অ্যাপ পরিবর্তন করুন"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"স্ক্রিনের নিচের থেকে উপরের দিকে সোয়াইপ করে ধরে থাকুন, তারপরে ছেড়ে দিন"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"শাবাশ!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"সব প্রস্তুত"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"সম্পূর্ণ হয়েছে"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"সেটিংস"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"\'টাস্কবার\' দেখানো হয়েছে"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"\'টাস্কবার\' লুকানো রয়েছে"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"নেভিগেশন বার"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"সবসময় টাস্কবার দেখুন"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"\'নেভিগেশন\' মোড পরিবর্তন করুন"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"টাস্কবার ডিভাইডার"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"উপরে/বাঁদিকে সরান"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"নিচে/ডানদিকে সরান"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{আরও #টি অ্যাপ দেখুন।}one{আরও #টি অ্যাপ দেখুন।}other{আরও #টি অ্যাপ দেখুন।}}"</string> diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml index 56f2fa8851..691c72091e 100644 --- a/quickstep/res/values-bs/strings.xml +++ b/quickstep/res/values-bs/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Prijedlozi aplikacija su omogućeni"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Prijedlozi aplikacija su onemogućeni"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predviđena aplikacija: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotirajte uređaj"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rotirajte uređaj da završite vodič za navigaciju pokretima"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Prevucite s krajnjeg desnog ili krajnjeg lijevog ruba"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Prevucite s desnog ili lijevog ruba prema sredini ekrana i pustite"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Vodite računa da prevučete s krajnjeg desnog ili krajnjeg lijevog ruba."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Vodite računa da prevučete s desnog ili lijevog ruba prema sredini ekrana i pustite."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Naučili ste kako prevući zdesna da se vratite. Sljedeće naučite kako prebacivati između aplikacija."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Savladali ste pokret za vraćanje"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Pazite da ne prevučete preblizu donjem dijelu ekrana"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Savladali ste pokret za vraćanje."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Obratite pažnju da ne prevučete preblizu donjem dijelu ekrana."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Promijenite osjetljivost pokreta za povratak u Postavkama"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Prevucite da se vratite"</string> - <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Da se vratite na prethodni ekran, prevucite s lijevog ili desnog ruba prema sredini ekrana."</string> + <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Da se vratite na posljednji ekran, prevucite s lijevog ili desnog ruba prema sredini ekrana."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Da se vratite na posljednji ekran, prevucite s 2 prsta od lijevog ili desnog ruba do sredine ekrana."</string> - <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Vratite se nazad"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Prevucite s lijevog ili desnog ruba prema sredini ekrana"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Prevucite prema gore s donjeg ruba ekrana"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Pazite da ne zastanete prije puštanja"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Prevucite ravno nagore"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Savladali ste pokret za otvaranje početnog ekrana. Sljedeće naučite kako se vratiti."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Savladali ste pokret za otvaranje početnog ekrana"</string> + <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Nazad"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Trebate prevući prema gore s donjeg ruba ekrana."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Obratite pažnju da ne zastanete prije puštanja."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Trebate prevući ravno prema gore."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Savladali ste pokret za otvaranje početnog ekrana. Sljedeće naučite kako se vratiti."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Savladali ste pokret za otvaranje početnog ekrana."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Prevucite da odete na početni ekran"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Prevucite s dna ekrana prema gore. Tim pokretom uvijek idete na početni ekran."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Prevucite s 2 prsta od dna ekrana. Tim pokretom uvijek idete na početni ekran"</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Idite na početni ekran"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Prevucite s dna ekrana prema gore"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Sjajno!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Vodite računa da prevučete s donjeg ruba ekrana prema gore"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Pokušajte zadržati prozor duže prije puštanja"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Prevucite ravno nagore, a zatim zastanite"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Odlazak na početni ekran"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Idite na početni ekran u bilo kojem trenutku, prevucite s donjeg dijela ekrana nagore"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Trebate prevući prema gore s donjeg ruba ekrana."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Pokušajte zadržati prozor duže prije puštanja."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Trebate prevući ravno prema gore, a zatim zastati."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Naučili ste kako koristiti pokrete. Idite u Postavke da isključite pokrete."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Savladali ste pokret za prebacivanje između aplikacija"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Savladali ste pokret za prebacivanje između aplikacija."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Prevucite da prebacujete između aplikacija"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Da se prebacujete između aplikacija, prevucite s dna ekrana nagore, zadržite, a zatim pustite."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Da prebacujete između aplikacija, prevucite s dna ekrana prema gore, zadržite, a zatim pustite."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Da se prebacujete između aplikacija, prevucite s 2 prsta od dna ekrana, zadržite, a zatim pustite."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Prebacujte se između aplikacija"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Prevucite s dna ekrana prema gore, zadržite, a zatim pustite"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Odlično!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Sve je spremno"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Gotovo"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Postavke"</string> @@ -88,7 +82,7 @@ <string name="allset_title" msgid="5021126669778966707">"Sve je spremno!"</string> <string name="allset_hint" msgid="459504134589971527">"Prevucite prema gore da odete na početni ekran"</string> <string name="allset_button_hint" msgid="2395219947744706291">"Dodirnite dugme za početni ekran da odete napočetni ekran"</string> - <string name="allset_description_generic" msgid="5385500062202019855">"Spremni ste da počnete koristiti <xliff:g id="DEVICE">%1$s</xliff:g>"</string> + <string name="allset_description_generic" msgid="5385500062202019855">"Sve je spremno da počnete koristiti uređaj <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="default_device_name" msgid="6660656727127422487">"uređaj"</string> <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Postavke navigiranja sistemom"</annotation></string> <string name="action_share" msgid="2648470652637092375">"Dijeli"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Traka zadataka je prikazana"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Traka zadataka je sakrivena"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigaciona traka"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Uvijek prikaži traku zadataka"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Promijeni način navigacije"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Razdjelnik trake zadataka"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premjesti gore lijevo"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premjesti dolje desno"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Prikaži još # aplikaciju.}one{Prikaži još # aplikaciju.}few{Prikaži još # aplikacije.}other{Prikaži još # aplikacija.}}"</string> diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml index 6dff768e82..919712aaaa 100644 --- a/quickstep/res/values-ca/strings.xml +++ b/quickstep/res/values-ca/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Els suggeriments d\'aplicacions estan activats"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Els suggeriments d\'aplicacions estan desactivats"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicció d\'aplicació: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Gira el dispositiu"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Gira el dispositiu per completar el tutorial de navegació amb gestos"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Assegura\'t de lliscar des de l\'extrem dret o esquerre de la pantalla."</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Assegura\'t de lliscar des de la vora dreta o esquerra cap al centre de la pantalla i deixar anar"</string> - <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Has après a lliscar des de la dreta per tornar enrere. Ara, descobreix com pots canviar d\'aplicació."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Has completat el gest per tornar enrere"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Assegura\'t de no lliscar massa a prop de la part inferior de la pantalla."</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Assegura\'t de lliscar des de l\'extrem dret o esquerre de la pantalla."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Assegura\'t de lliscar des de la vora dreta o esquerra cap al centre de la pantalla i deixar anar."</string> + <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Has après com pots lliscar des de la dreta per tornar enrere. Ara, descobreix com pots canviar d\'app."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Has completat el gest per tornar enrere."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Assegura\'t de no lliscar massa a prop de la part inferior de la pantalla."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Per canviar la sensibilitat del gest, ves a Configuració"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Llisca per anar enrere"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Per tornar a la darrera pantalla, llisca des de la vora esquerra o dreta cap al centre de la pantalla."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Per tornar a la pantalla anterior, llisca amb dos dits des de l\'extrem esquerre o dret cap al centre de la pantalla."</string> - <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Torna enrere"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Llisca des de la vora esquerra o dreta cap al centre de la pantalla."</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Assegura\'t de lliscar cap amunt des de la part inferior de la pantalla"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Assegura\'t de no aturar-te abans de deixar anar."</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Assegura\'t de lliscar recte cap amunt."</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Has completat el gest per anar a la pantalla d\'inici. Ara, descobreix com pots tornar enrere."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Has completat el gest per anar a la pantalla d\'inici"</string> + <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Torna"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Assegura\'t de lliscar des de la vora inferior de la pantalla."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Assegura\'t de no aturar-te abans de deixar anar."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Assegura\'t de lliscar directament cap amunt."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Has completat el gest per anar a la pantalla d\'inici. Ara, descobreix com pots tornar enrere."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Has completat el gest per anar a la pantalla d\'inici."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Llisca per anar a la pantalla d\'inici"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Llisca cap amunt des de la part inferior de la pantalla. Aquest gest et porta a la pantalla d\'inici."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Llisca amb dos dits cap amunt des de la part inferior. Aquest gest sempre porta a la pantalla d\'inici."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Ves a la pàgina d\'inici"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Llisca cap amunt des de la part inferior de la pantalla."</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Ben fet!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Assegura\'t de lliscar cap amunt des de la part inferior de la pantalla."</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Prova de mantenir premuda la finestra durant més temps abans de deixar-la anar"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Assegura\'t de lliscar directament cap amunt i després aturar-te"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Per anar a la pantalla d\'inici en qualsevol moment, llisca cap amunt des la part inferior"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Assegura\'t de lliscar des de la vora inferior de la pantalla."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Prova de mantenir premuda la finestra durant més temps abans de deixar-la anar."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Assegura\'t de lliscar directament cap amunt i després aturar-te."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Has après a utilitzar els gestos. Per desactivar-los, ves a Configuració."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Has completat el gest per canviar d\'aplicació"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Has completat el gest per canviar d\'aplicació."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Llisca per canviar d\'aplicació"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Per canviar entre aplicacions, llisca cap amunt des de la part inferior, mantén premut i deixa anar."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Per canviar entre apps, llisca amb dos dits cap amunt des de la part inferior, mantén i deixa anar."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Canvia d\'aplicació"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Llisca cap amunt des de la part inferior de la pantalla, mantén premut i, a continuació, deixa anar."</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Ben fet!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Tot a punt"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Fet"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Configuració"</string> @@ -88,7 +82,7 @@ <string name="allset_title" msgid="5021126669778966707">"Tot a punt!"</string> <string name="allset_hint" msgid="459504134589971527">"Llisca cap amunt per anar a la pàgina d\'inici"</string> <string name="allset_button_hint" msgid="2395219947744706291">"Toca el botó d\'inici per anar a la pantalla d\'inici"</string> - <string name="allset_description_generic" msgid="5385500062202019855">"Ja pots començar a utilitzar el <xliff:g id="DEVICE">%1$s</xliff:g>"</string> + <string name="allset_description_generic" msgid="5385500062202019855">"Ja pots començar a utilitzar <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="default_device_name" msgid="6660656727127422487">"dispositiu"</string> <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configuració de navegació del sistema"</annotation></string> <string name="action_share" msgid="2648470652637092375">"Comparteix"</string> @@ -103,11 +97,11 @@ <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Omet"</string> <string name="accessibility_rotate_button" msgid="4771825231336502943">"Gira la pantalla"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Informació sobre Barra de tasques"</string> - <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Arrossega una aplicació al costat per utilitzar-ne dues alhora"</string> + <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Arrossega una app al costat per utilitzar 2 apps alhora"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Llisca lentament cap amunt per mostrar la Barra de tasques"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Obtén suggeriments d\'aplicacions basats en la teva rutina"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"A Configuració, activa la navegació amb gestos per amagar la Barra de tasques automàticament"</string> - <string name="taskbar_edu_features" msgid="3320337287472848162">"Treu més partit de la Barra de tasques"</string> + <string name="taskbar_edu_features" msgid="3320337287472848162">"Fes més coses amb la Barra de tasques"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Tanca"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Fet"</string> <string name="taskbar_button_home" msgid="2151398979630664652">"Inici"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Es mostra la Barra de tasques"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"S\'ha amagat la Barra de tasques"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegació"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Mostra sempre Barra de tasques"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Canvia el mode de navegació"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Separador de la Barra de tasques"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mou a la part superior o a l\'esquerra"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mou a la part inferior o a la dreta"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Mostra # aplicació més.}other{Mostra # aplicacions més.}}"</string> diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml index 40f8c3e347..5ac7e7eaa8 100644 --- a/quickstep/res/values-cs/strings.xml +++ b/quickstep/res/values-cs/strings.xml @@ -44,57 +44,51 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Návrhy aplikací jsou povoleny"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Návrhy aplikací jsou zakázány"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Předpokládaná aplikace: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Otočte zařízení"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Pokud chcete dokončit výukový program navigace gesty, otočte zařízení"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Přejeďte prstem z úplného pravého nebo levého okraje obrazovky"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Přejeďte prstem z pravého nebo levého okraje doprostřed obrazovky a zdvihněte prst"</string> - <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Naučili jste se, jak se vrátit zpět přejetím prstem zprava. Teď se naučíte přepínat mezi aplikacemi."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Dokončili jste gesto pro přechod zpět"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Dejte pozor, abyste prstem nepřejížděli moc blízko ke spodnímu okraji obrazovky"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Přejeďte prstem z úplného pravého nebo levého okraje obrazovky."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Přejeďte prstem z pravého nebo levého okraje doprostřed obrazovky a zdvihněte prst."</string> + <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Naučili jste se, jak se vrátit zpět přejetím prstem zprava. Teď se naučíte přepínat aplikace."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Dokončili jste gesto pro přechod zpět."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Dejte pozor, abyste prstem nepřejížděli moc blízko ke spodnímu okraji obrazovky."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Citlivost gesta pro přechod zpět můžete změnit v Nastavení"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Přejetím prstem se vrátíte zpět"</string> - <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Na předchozí obrazovku se vrátíte tak, že přejedete prstem z levého nebo pravého okraje obrazovky doprostřed."</string> + <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"K návratu na poslední obrazovku přejeďte prstem z levého nebo pravého okraje obrazovky doprostřed."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Pokud se chcete vrátit na poslední obrazovku, přejeďte dvěma prsty z levého nebo pravého okraje doprostřed obrazovky."</string> - <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Návrat zpět"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Přejeďte prstem z levého nebo pravého okraje do středu obrazovky"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Přejeďte prstem nahoru z dolního okraje obrazovky"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Před zdvihnutím prstu nedělejte pauzu"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Přejeďte prstem přímo nahoru"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Dokončili jste gesto pro přechod na plochu. Teď se naučíte vrátit se zpět."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Dokončili jste gesto pro přechod na plochu"</string> + <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Zpět"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Přejeďte prstem nahoru z dolního okraje obrazovky."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Před zdvihnutím prstu nedělejte pauzu."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Přejeďte prstem přímo nahoru."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Dokončili jste gesto pro přechod na plochu. Teď se naučíte vrátit se zpět."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Dokončili jste gesto pro přechod na plochu."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Přechod na plochu přejetím prstem"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Přejeďte prstem ze spodní části obrazovky nahoru. Tímto gestem se vždy dostanete na plochu."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Přejeďte dvěma prsty z dolního okraje obrazovky nahoru. Tímto gestem se vždy dostanete na plochu."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Přechod na plochu"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Přejeďte prstem ze spodní části obrazovky nahoru"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Výborně!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Přejeďte prstem nahoru z dolního okraje obrazovky"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Zkuste podržet okno delší dobu, než ho uvolníte"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Přejeďte prstem přímo nahoru a pak udělejte pauzu"</string> - <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Naučili jste se používat gesta. Vypnout je můžete v nastavení."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Dokončili jste gesto pro přepínání aplikací"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Přejít na plochu"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Na plochu kdykoli přejdete přejetím prstem ze spodní části obrazovky nahoru"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Přejeďte prstem nahoru z dolního okraje obrazovky."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Zkuste podržet okno delší dobu, než ho uvolníte."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Přejeďte prstem přímo nahoru a pak udělejte pauzu."</string> + <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Naučili jste se používat gesta. Gesta můžete vypnout v nastavení."</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Dokončili jste gesto pro přepínání aplikací."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Přepínání aplikací přejetím prstem"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Přejeďte nahoru z dolního okraje obrazovky, podržte obrazovku a uvolněte."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Přepínání mezi aplikacemi: Přejeďte nahoru z dolního okraje obrazovky, podržte obrazovku a uvolněte."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Přepínání mezi aplikacemi: Přejeďte dvěma prsty nahoru z dolního okraje obrazovky, podržte obrazovku a uvolněte."</string> - <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Přepínání aplikací"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Přejeďte prstem nahoru z dolního okraje obrazovky, podržte obrazovku a potom prst uvolněte"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Výborně!"</string> + <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Přepnout aplikace"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Vše je nastaveno"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Hotovo"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Nastavení"</string> - <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Zkuste to znovu"</string> + <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Zkusit znovu"</string> <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Skvělé!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"Výukový program <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"Hotovo!"</string> <string name="allset_hint" msgid="459504134589971527">"Přejetím nahoru se vrátíte na plochu"</string> <string name="allset_button_hint" msgid="2395219947744706291">"Klepnutím na tlačítko plochy se vrátíte na plochu"</string> - <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> můžete začít používat"</string> + <string name="allset_description_generic" msgid="5385500062202019855">"Jste připraveni začít používat <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="default_device_name" msgid="6660656727127422487">"zařízení"</string> <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Nastavení navigace v systému"</annotation></string> <string name="action_share" msgid="2648470652637092375">"Sdílet"</string> <string name="action_screenshot" msgid="8171125848358142917">"Snímek obrazovky"</string> <string name="action_split" msgid="2098009717623550676">"Rozdělit"</string> - <string name="toast_split_select_app" msgid="8464310533320556058">"Obrazovku rozdělíte klepnutím na jinou aplikaci"</string> + <string name="toast_split_select_app" msgid="8464310533320556058">"Klepnutím na jinou aplikaci rozdělíte obrazovku"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Vyberte podporovanou aplikaci"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikace nebo organizace zakazuje tuto akci"</string> <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Přeskočit výukový program k navigaci?"</string> @@ -105,9 +99,9 @@ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Informace o panelu aplikací"</string> <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Přetáhněte aplikaci na stranu a používejte tak dvě najednou"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Panel aplikací zobrazíte pomalým přejetím prstem nahoru"</string> - <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Dostávejte návrhy aplikací podle toho, jaké používáte"</string> + <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Získejte návrhy aplikací na základě vašeho používání"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Pokud chcete panel aplikací automaticky skrýt, zapněte v Nastavení navigaci gesty"</string> - <string name="taskbar_edu_features" msgid="3320337287472848162">"Více možností s panelem aplikací"</string> + <string name="taskbar_edu_features" msgid="3320337287472848162">"Pomocí panelu aplikací můžete dělat více věcí"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Zavřít"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Hotovo"</string> <string name="taskbar_button_home" msgid="2151398979630664652">"Domů"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Panel aplikací je zobrazen"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Panel aplikací je skrytý"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigační panel"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Vždy zobrazovat panel aplikací"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Změnit režim navigace"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Rozdělovač panelu aplikací"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Přesunout doleva nahoru"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Přesunout doprava dolů"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Zobrazit # další aplikaci.}few{Zobrazit # další aplikace.}many{Zobrazit # další aplikace.}other{Zobrazit # dalších aplikací.}}"</string> diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml index 7c786f31c4..d0cea4c874 100644 --- a/quickstep/res/values-da/strings.xml +++ b/quickstep/res/values-da/strings.xml @@ -44,47 +44,41 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Appforslag er aktiveret"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Appforslag er deaktiveret"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App, du forventes at skulle bruge: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotér din enhed"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rotér enheden for at gennemgå vejledningen i navigation med bevægelser"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Stryg fra kanten yderst til højre eller venstre"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Stryg fra højre eller venstre kant mod midten af skærmen, og løft fingeren"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Stryg fra kanten yderst til højre eller venstre."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Stryg fra højre eller venstre kant mod midten af skærmen, og løft fingeren."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Du har lært, hvordan du stryger fra højre for at gå tilbage. Nu skal du se, hvordan du skifter app."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Du har fuldført bevægelsen for Gå tilbage"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Undgå at stryge for tæt på bunden af skærmen"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Du har fuldført bevægelsen for Gå tilbage."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Undgå at stryge for tæt på bunden af skærmen."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Juster følsomheden for bevægelsen Gå tilbage i Indstillinger"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Stryg for at gå tilbage"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Stryg mod midten af skærmen fra venstre eller højre kant for at gå tilbage til den seneste skærm."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Du kan gå tilbage til forrige skærm ved at stryge fra venstre eller højre kant mod midten af skærmen med 2 fingre."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Gå tilbage"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Stryg fra venstre eller højre kant til midten af skærmen"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Stryg opad fra bunden af skærmen"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Undlad at holde fingeren stille, indtil du løfter fingeren"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Stryg lige opad"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Du har fuldført bevægelsen for Gå til startskærmen. Som det næste kan du se, hvordan du går tilbage."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Du har fuldført bevægelsen for Gå til startskærmen"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Stryg opad fra bunden af skærmen."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Undlad at holde fingeren stille, indtil du løfter fingeren."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Stryg lige opad."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Du har fuldført bevægelsen for Gå til startskærmen. Som det næste kan du se, hvordan du går tilbage."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Du har fuldført bevægelsen for Gå til startskærmen."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Stryg for at gå til startskærmen"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Stryg opad fra bunden af skærmen. Denne bevægelse åbner altid startskærmen."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Stryg opad med 2 fingre fra bunden af skærmen. Denne bevægelse åbner altid startskærmen."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Gå til startskærmen"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Stryg opad fra bunden af skærmen"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Flot!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Stryg opad fra bunden af skærmen"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Prøv at holde fingeren nede på vinduet i længere tid, inden du løfter den"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Stryg lige opad, og hold derefter fingeren stille"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Du kan altid gå til startskærmen ved at stryge opad fra bunden af skærmen"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Stryg opad fra bunden af skærmen."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Prøv at holde fingeren nede på vinduet i længere tid, inden du løfter den."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Stryg lige opad, og hold derefter fingeren stille."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Du har lært, hvordan du bruger bevægelser. Du kan aktivere bevægelser i Indstillinger."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Du har fuldført bevægelsen for at skifte mellem apps"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Du har fuldført bevægelsen for Skift app."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Stryg for at skifte app"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Skift mellem apps ved at stryge opad fra bunden af skærmen, holde fingeren stille og løfte den."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Skift mellem apps ved at stryge opad fra bunden af skærmen med 2 fingre, holde dem nede og slippe."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Skift mellem apps"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Stryg opad fra bunden af skærmen, hold fingeren nede, og slip"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Flot!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Så er du klar"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Luk"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Indstillinger"</string> <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Prøv igen"</string> <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Sådan!"</string> - <string name="gesture_tutorial_step" msgid="1279786122817620968">"Vejledning <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> + <string name="gesture_tutorial_step" msgid="1279786122817620968">"Selvstudie <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"Alt er parat!"</string> <string name="allset_hint" msgid="459504134589971527">"Stryg opad for at gå til startsiden"</string> <string name="allset_button_hint" msgid="2395219947744706291">"Tryk på knappen Hjem for at gå til din startskærm"</string> @@ -97,7 +91,7 @@ <string name="toast_split_select_app" msgid="8464310533320556058">"Tryk på en anden app for at bruge opdelt skærm"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Vælg en anden app for at bruge opdelt skærm"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller din organisation tillader ikke denne handling"</string> - <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Vil du springe vejledningen for navigation over?"</string> + <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Vil du springe selvstudiet for navigation over?"</string> <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Du kan finde dette senere i appen <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Annuller"</string> <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Spring over"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Proceslinjen vises"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Proceslinjen er skjult"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigationslinje"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Vis altid proceslinjen"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Skift navigationstilstand"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Opdeling af proceslinjen"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flyt til toppen eller venstre side"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flyt til bunden eller højre side"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Vis # app mere.}one{Vis # app mere.}other{Vis # apps mere.}}"</string> diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml index 5fd1232730..d906ab80d4 100644 --- a/quickstep/res/values-de/strings.xml +++ b/quickstep/res/values-de/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Funktion „App-Vorschläge“ aktiviert"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Funktion \"App-Vorschläge\" deaktiviert"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Vorgeschlagene App: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Gerät drehen"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Bitte drehe dein Gerät, um die Anleitung für die Bedienung über Gesten abzuschließen"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Wische vom äußersten rechten oder linken Displayrand"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Wische vom rechten oder linken Displayrand zur Displaymitte und lass los"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Wische vom äußersten rechten oder linken Displayrand."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Wische vom rechten oder linken Displayrand zur Displaymitte und lass los."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Du hast jetzt gelernt, vom rechten Displayrand aus zu wischen, um zurückzugehen. Gleich erfährst du, wie man zwischen Apps wechselt."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Du hast den Schritt für die „Zurück“-Touch-Geste abgeschlossen"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Wische nicht zu nah an den unteren Displayrand"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Du hast die „Zurück“-Touch-Geste abgeschlossen."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Wische nicht zu nah am unteren Displayrand."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Du kannst die Empfindlichkeit von „Zurück“ in den Einstellungen ändern"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Zum Zurückgehen wischen"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Wenn du zum letzten Bildschirm zurückgehen möchtest, wische vom linken oder rechten Rand zur Mitte."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Wenn du zum letzten Bildschirm zurückgehen möchtest, wische mit zwei Fingern vom linken oder rechten Displayrand zur Mitte."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Zurück"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Wische vom linken oder rechten Displayrand bis zur Mitte des Displays"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Wische vom unteren Displayrand nach oben"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Achte darauf, nicht innezuhalten, bevor du loslässt"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Wische gerade nach oben"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Du hast den Schritt für die „Zum Startbildschirm“-Geste abgeschlossen. Jetzt lernst du, wie du zurückgehst."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Du hast den Schritt für die „Startbildschirm“-Touch-Geste abgeschlossen"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Wische vom unteren Displayrand nach oben."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Achte darauf, nicht innezuhalten, bevor du loslässt."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Wische gerade nach oben."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Du hast die Touch-Geste zum Aufrufen des Startbildschirms abgeschlossen. Gleich erfährst du, wie du zurückgelangst."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Du hast die Touch-Geste zum Aufrufen des Startbildschirms abgeschlossen."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Den Startbildschirm aufrufen"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Wenn du zum Startbildschirm gehen möchtest, wische einfach vom unteren Displayrand nach oben."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Wische mit zwei Fingern vom unteren Displayrand nach oben. So gelangst du immer zum Startbildschirm."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Zum StartU+00ADbildschirm"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Wische vom unteren Displayrand nach oben"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Gut gemacht!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Wische vom unteren Displayrand nach oben"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Halte das Fenster länger gedrückt, bevor du es loslässt"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Wische gerade nach oben und halte dann inne"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Zum Startbildschirm"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Du kannst jederzeit zum Startbildschirm gehen, indem du vom unteren Displayrand nach oben wischst"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Wische vom unteren Displayrand nach oben."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Versuche, das Fenster länger festzuhalten, bevor du es loslässt."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Wische gerade nach oben und halte dann inne."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Nun weißt du, wie Touch-Gesten funktionieren. Du kannst sie in den Einstellungen deaktivieren."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Du hast den Schritt für die „Apps wechseln“-Touch-Geste abgeschlossen"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Du hast die „Apps wechseln“-Touch-Geste abgeschlossen."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Zwischen Apps wechseln"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Wische auf dem Display von unten nach oben, halte den Finger gedrückt und lass dann los, um zwischen Apps zu wechseln."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Wische zum App-Wechseln mit zwei Fingern vom unteren Displayrand nach oben, halte und lass dann los."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Apps wechseln"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Wische auf dem Display von unten nach oben, halte es gedrückt und lass es dann los"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Perfekt!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Fertig"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Fertig"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Einstellungen"</string> @@ -94,8 +88,8 @@ <string name="action_share" msgid="2648470652637092375">"Teilen"</string> <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string> <string name="action_split" msgid="2098009717623550676">"Teilen"</string> - <string name="toast_split_select_app" msgid="8464310533320556058">"Für Splitscreen auf weitere App tippen"</string> - <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Für Splitscreen andere App auswählen"</string> + <string name="toast_split_select_app" msgid="8464310533320556058">"Für „Geteilter Bildschirm“ auf weitere App tippen"</string> + <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Für geteilten Bildschirm andere App auswählen"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Die App oder deine Organisation lässt diese Aktion nicht zu"</string> <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Tutorial zur Bedienung überspringen?"</string> <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Du findest es später auch in der <xliff:g id="NAME">%1$s</xliff:g> App"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskleiste eingeblendet"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskleiste ausgeblendet"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigationsleiste"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Taskleiste immer anzeigen"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Navigationsmodus ändern"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taskleisten-Teiler"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Nach oben / Nach links verschieben"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Nach unten / Nach rechts verschieben"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# weitere App anzeigen.}other{# weitere Apps anzeigen.}}"</string> diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml index d1c16ba3fe..0fa67fe972 100644 --- a/quickstep/res/values-el/strings.xml +++ b/quickstep/res/values-el/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Οι προτεινόμενες εφαρμογές ενεργοποιήθηκαν"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Οι προτεινόμενες εφαρμογές είναι απενεργοποιημένες"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Εφαρμογή από πρόβλεψη: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Περιστρέψτε τη συσκευή σας"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Περιστρέψτε τη συσκευή σας για να ολοκληρώσετε τον οδηγό πλοήγησης με κινήσεις"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Φροντίστε να σύρετε από το άκρο της δεξιάς ή της αριστερής πλευράς."</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Σύρετε από το δεξί ή το αριστερό άκρο προς το κέντρο της οθόνης και απομακρύνετε το δάχτυλό σας"</string> - <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Μάθατε πώς να σύρετε από τα δεξιά για επιστροφή. Τώρα, μάθετε πώς να κάνετε εναλλαγή εφαρμογών."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Ολοκληρώσατε την κίνηση επιστροφής"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Φροντίστε να μην σύρετε υπερβολικά κοντά στο κάτω μέρος της οθόνης"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Φροντίστε να σύρετε από το άκρο της δεξιάς ή της αριστερής πλευράς."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Σύρετε από το δεξί ή αριστερό άκρο προς το μέσο της οθόνης και απομακρύνετε το δάχτυλό σας."</string> + <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Μάθατε πώς να σύρετε από τα δεξιά για επιστροφή. Στη συνέχεια, μάθετε πώς να κάνετε εναλλαγή εφαρμ."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Ολοκληρώσατε την κίνηση επιστροφής."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Φροντίστε να μην σύρετε υπερβολικά κοντά στο κάτω μέρος της οθόνης."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Μεταβείτε στις Ρυθμίσεις για αλλαγή ευαισθ. κίνησης επιστρ."</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Σύρετε για επιστροφή"</string> - <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Για να επιστρέψετε στην τελευταία οθόνη, σύρετε από το αριστερό ή το δεξί άκρο προς το μέσο της οθόνης."</string> + <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Για να επιστρέψετε στην τελευτ. οθόνη, σύρετε από το αριστ. ή το δεξί άκρο προς το μέσο της οθόνης."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Για να επιστρέψετε στην προηγούμενη οθόνη, σύρετε με 2 δάχτυλα από την αριστερή ή δεξιά άκρη προς τη μέση της οθόνης."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Επιστροφή"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Σύρετε από το αριστερό ή το δεξί άκρο προς το κέντρο της οθόνης"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Φροντίστε να σύρετε προς τα επάνω από το κάτω άκρο της οθόνης"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Φροντίστε να μην διακόψετε την κίνηση προτού απομακρύνετε τα δάχτυλά σας"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Φροντίστε να σύρετε ευθεία προς τα επάνω"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Ολοκληρώσατε την κίνηση μετάβασης στην αρχική οθόνη Στη συνέχεια, μάθετε την κίνηση επιστροφής."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Ολοκληρώσατε την κίνηση μετάβασης στην αρχική οθόνη"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Φροντίστε να σύρετε προς τα επάνω από το κάτω άκρο της οθόνης."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Φροντίστε να μην κάνετε παύση προτού απομακρύνετε τα δάχτυλά σας."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Φροντίστε να σύρετε απευθείας προς τα επάνω."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Ολοκληρώσατε την κίνηση μετάβασης στην αρχική οθόνη. Στη συνέχεια, μάθετε πώς να κάνετε επιστροφή."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Ολοκληρώσατε την κίνηση μετάβασης στην αρχική οθόνη."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Σύρετε για μετάβαση στην αρχική οθόνη"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Σύρετε προς τα πάνω από το κάτω μέρος της οθόνης. Αυτή η κίνηση σάς μεταφέρει πάντα στην αρχ. οθόνη."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Σύρετε από το κάτω άκρο προς τα πάνω με 2 δάχτ. Αυτή η κίνηση σάς μεταφέρει πάντα στην αρχική οθόνη."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Αρχική"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Σύρετε προς τα επάνω από το κάτω μέρος της οθόνης"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Μπράβο!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Φροντίστε να σύρετε προς τα επάνω από το κάτω άκρο της οθόνης"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Δοκιμάστε να κρατήσετε περισσότερο το παράθυρο προτού το αφήσετε."</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Φροντίστε να σύρετε ευθεία προς τα επάνω και έπειτα σταματήστε την κίνηση"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Για μετάβαση στην αρχική οθόνη ανά πάσα στιγμή, σύρετε προς τα επάνω από το κάτω μέρος της οθόνης"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Φροντίστε να σύρετε προς τα επάνω από το κάτω άκρο της οθόνης."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Δοκιμάστε να κρατήσετε περισσότερο το παράθυρο προτού απελευθερώσετε."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Φροντίστε να σύρετε απευθείας προς τα επάνω και έπειτα κάντε παύση."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Μάθατε πώς να χρησιμοποιείτε κινήσεις. Μεταβείτε στις Ρυθμίσεις για απενεργοποίηση των κινήσεων."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Ολοκληρώσατε την κίνηση εναλλαγής εφαρμογών"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Ολοκληρώσατε την κίνηση εναλλαγής εφαρμογών."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Σύρετε για εναλλαγή εφαρμογών"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Για εναλλαγή εφαρμογών, σύρετε προς τα πάνω από το κάτω μέρος της οθόνης, πατήστε παρατεταμένα και μετά αφήστε."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Για εναλλαγή μεταξύ εφαρμογών, σύρετε από κάτω προς τα πάνω με 2 δάχτ., κρατήστε και έπειτα αφήστε."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Εναλλαγή μεταξύ εφαρμογών"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Σύρετε προς τα πάνω από το κάτω μέρος της οθόνης, κρατήστε τα δάχτυλα επάνω στην οθόνη και μετά απομακρύνετέ τα"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Μπράβο!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Όλα είναι έτοιμα"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Τέλος"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Ρυθμίσεις"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Η γραμμή εργαλείων εμφανίζεται"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Η γραμμή εργαλείων είναι κρυφή"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Γραμμή πλοήγησης"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Εμφ. πάντα σε Γραμμή εργαλείων"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Αλλαγή τρόπου πλοήγησης"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Διαχωριστικό Γραμμής εργαλείων"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Μετακίνηση επάνω/αριστερά"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Μετακίνηση κάτω/δεξιά"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Εμφάνιση # ακόμα εφαρμογής.}other{Εμφάνιση # ακόμα εφαρμογών.}}"</string> diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml index c2b000e153..ec90faf5a8 100644 --- a/quickstep/res/values-en-rAU/strings.xml +++ b/quickstep/res/values-en-rAU/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App suggestions enabled"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App suggestions are disabled"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicted app: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotate your device"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Please rotate your device to complete the gesture navigation tutorial"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Make sure you swipe from the far-right or far-left edge"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Make sure you swipe from the right or left edge to the middle of the screen and let go"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Make sure that you swipe from the far-right or far-left edge."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Make sure that you swipe from the right or left edge to the middle of the screen and let go."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"You\'ve learned how to swipe from the right to go back. Next, learn how to switch apps."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"You completed the go back gesture"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Make sure you don\'t swipe too close to the bottom of the screen"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"You completed the go back gesture."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Make sure that you don\'t swipe too close to the bottom of the screen."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"To change sensitivity of the back gesture, go to Settings"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Swipe to go back"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"To go back to the last screen, swipe from the left or right edge to the middle of the screen."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"To go back to the last screen, swipe with two fingers from the left or right edge to the middle of the screen."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Go back"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Swipe from the left or right edge to the middle of the screen"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Make sure you swipe up from the bottom edge of the screen"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Make sure you don\'t pause before letting go"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Make sure you swipe straight up"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"You completed the go home gesture. Next up, learn how to go back."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"You completed the go home gesture"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Make sure that you swipe up from the bottom edge of the screen."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Make sure that you don\'t pause before letting go."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Make sure that you swipe straight up."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"You\'ve completed the go home gesture. Next, learn how to go back."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"You completed the go home gesture."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Swipe to go home"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swipe up from the bottom of your screen. This gesture always takes you to the home screen."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Swipe up with two fingers from the bottom of the screen. This gesture always takes you to the home screen."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Go home"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Swipe up from the bottom of your screen"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Great work!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Make sure you swipe up from the bottom edge of the screen"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Try holding the window for longer before releasing"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Make sure that you swipe straight up, then pause"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"To go to your home screen at any time, swipe up from the bottom of your screen"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Make sure that you swipe up from the bottom edge of the screen."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Try holding the window for longer before releasing."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Make sure that you swipe straight up, then pause."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"You\'ve learned how to use gestures. To turn off gestures, go to Settings."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"You completed the switch apps gesture"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"You completed the switch apps gesture."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swipe to switch apps"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"To switch between apps, swipe up from the bottom of your screen, hold, then release."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"To switch between apps, swipe up with two fingers from the bottom of your screen, hold, then release."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Switch apps"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Swipe up from the bottom of your screen, hold, then release"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Well done!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"All set"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Done"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Settings"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar shown"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar hidden"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taskbar divider"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Show # more app.}other{Show # more apps.}}"</string> diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml index 0def6cfff5..01599c4ec3 100644 --- a/quickstep/res/values-en-rCA/strings.xml +++ b/quickstep/res/values-en-rCA/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App suggestions enabled"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App suggestions are disabled"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicted app: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotate your device"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Please rotate your device to complete the gesture navigation tutorial"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Make sure you swipe from the far-right or far-left edge"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Make sure you swipe from the right or left edge to the middle of the screen and let go"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Make sure you swipe from the far-right or far-left edge."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Make sure you swipe from the right or left edge to the middle of the screen and let go."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"You learned how to swipe from the right to go back. Next up, learn how to switch apps."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"You completed the go back gesture"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Make sure you don\'t swipe too close to the bottom of the screen"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"You completed the go back gesture."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Make sure you don\'t swipe too close to the bottom of the screen."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"To change the sensitivity of the back gesture, go to Settings"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Swipe to go back"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"To go back to the last screen, swipe from the left or right edge to the middle of the screen."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"To go back to the last screen, swipe with 2 fingers from the left or right edge to the middle of the screen."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Go back"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Swipe from the left or right edge to the middle of the screen"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Make sure you swipe up from the bottom edge of the screen"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Make sure you don\'t pause before letting go"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Make sure you swipe straight up"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"You completed the go home gesture. Next up, learn how to go back."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"You completed the go home gesture"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Make sure you swipe up from the bottom edge of the screen."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Make sure you don\'t pause before letting go."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Make sure you swipe straight up."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"You completed the go Home gesture. Next up, learn how to go back."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"You completed the go Home gesture."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Swipe to go home"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swipe up from the bottom of your screen. This gesture always takes you to the Home screen."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Swipe up with 2 fingers from the bottom of the screen. This gesture always takes you to the Home screen."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Go home"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Swipe up from the bottom of your screen"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Great job!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Make sure you swipe up from the bottom edge of the screen"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Try holding the window for longer before releasing"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Make sure you swipe straight up, then pause"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"To go to your home screen at any time, swipe up from the bottom of your screen"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Make sure you swipe up from the bottom edge of the screen."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Try holding the window for longer before releasing."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Make sure you swipe straight up, then pause."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"You learned how to use gestures. To turn off gestures, go to Settings."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"You completed the switch apps gesture"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"You completed the switch apps gesture."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swipe to switch apps"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"To switch between apps, swipe up from the bottom of your screen, hold, then release."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"To switch between apps, swipe up with 2 fingers from the bottom of your screen, hold, then release."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Switch apps"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Swipe up from the bottom of your screen, hold, then release"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Well done!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"All set"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Done"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Settings"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar shown"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar hidden"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taskbar Divider"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Show # more app.}other{Show # more apps.}}"</string> diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml index c2b000e153..ec90faf5a8 100644 --- a/quickstep/res/values-en-rGB/strings.xml +++ b/quickstep/res/values-en-rGB/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App suggestions enabled"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App suggestions are disabled"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicted app: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotate your device"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Please rotate your device to complete the gesture navigation tutorial"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Make sure you swipe from the far-right or far-left edge"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Make sure you swipe from the right or left edge to the middle of the screen and let go"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Make sure that you swipe from the far-right or far-left edge."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Make sure that you swipe from the right or left edge to the middle of the screen and let go."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"You\'ve learned how to swipe from the right to go back. Next, learn how to switch apps."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"You completed the go back gesture"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Make sure you don\'t swipe too close to the bottom of the screen"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"You completed the go back gesture."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Make sure that you don\'t swipe too close to the bottom of the screen."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"To change sensitivity of the back gesture, go to Settings"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Swipe to go back"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"To go back to the last screen, swipe from the left or right edge to the middle of the screen."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"To go back to the last screen, swipe with two fingers from the left or right edge to the middle of the screen."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Go back"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Swipe from the left or right edge to the middle of the screen"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Make sure you swipe up from the bottom edge of the screen"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Make sure you don\'t pause before letting go"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Make sure you swipe straight up"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"You completed the go home gesture. Next up, learn how to go back."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"You completed the go home gesture"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Make sure that you swipe up from the bottom edge of the screen."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Make sure that you don\'t pause before letting go."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Make sure that you swipe straight up."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"You\'ve completed the go home gesture. Next, learn how to go back."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"You completed the go home gesture."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Swipe to go home"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swipe up from the bottom of your screen. This gesture always takes you to the home screen."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Swipe up with two fingers from the bottom of the screen. This gesture always takes you to the home screen."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Go home"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Swipe up from the bottom of your screen"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Great work!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Make sure you swipe up from the bottom edge of the screen"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Try holding the window for longer before releasing"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Make sure that you swipe straight up, then pause"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"To go to your home screen at any time, swipe up from the bottom of your screen"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Make sure that you swipe up from the bottom edge of the screen."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Try holding the window for longer before releasing."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Make sure that you swipe straight up, then pause."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"You\'ve learned how to use gestures. To turn off gestures, go to Settings."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"You completed the switch apps gesture"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"You completed the switch apps gesture."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swipe to switch apps"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"To switch between apps, swipe up from the bottom of your screen, hold, then release."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"To switch between apps, swipe up with two fingers from the bottom of your screen, hold, then release."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Switch apps"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Swipe up from the bottom of your screen, hold, then release"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Well done!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"All set"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Done"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Settings"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar shown"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar hidden"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taskbar divider"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Show # more app.}other{Show # more apps.}}"</string> diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml index c2b000e153..ec90faf5a8 100644 --- a/quickstep/res/values-en-rIN/strings.xml +++ b/quickstep/res/values-en-rIN/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App suggestions enabled"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App suggestions are disabled"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicted app: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotate your device"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Please rotate your device to complete the gesture navigation tutorial"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Make sure you swipe from the far-right or far-left edge"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Make sure you swipe from the right or left edge to the middle of the screen and let go"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Make sure that you swipe from the far-right or far-left edge."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Make sure that you swipe from the right or left edge to the middle of the screen and let go."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"You\'ve learned how to swipe from the right to go back. Next, learn how to switch apps."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"You completed the go back gesture"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Make sure you don\'t swipe too close to the bottom of the screen"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"You completed the go back gesture."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Make sure that you don\'t swipe too close to the bottom of the screen."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"To change sensitivity of the back gesture, go to Settings"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Swipe to go back"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"To go back to the last screen, swipe from the left or right edge to the middle of the screen."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"To go back to the last screen, swipe with two fingers from the left or right edge to the middle of the screen."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Go back"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Swipe from the left or right edge to the middle of the screen"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Make sure you swipe up from the bottom edge of the screen"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Make sure you don\'t pause before letting go"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Make sure you swipe straight up"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"You completed the go home gesture. Next up, learn how to go back."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"You completed the go home gesture"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Make sure that you swipe up from the bottom edge of the screen."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Make sure that you don\'t pause before letting go."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Make sure that you swipe straight up."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"You\'ve completed the go home gesture. Next, learn how to go back."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"You completed the go home gesture."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Swipe to go home"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swipe up from the bottom of your screen. This gesture always takes you to the home screen."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Swipe up with two fingers from the bottom of the screen. This gesture always takes you to the home screen."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Go home"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Swipe up from the bottom of your screen"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Great work!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Make sure you swipe up from the bottom edge of the screen"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Try holding the window for longer before releasing"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Make sure that you swipe straight up, then pause"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"To go to your home screen at any time, swipe up from the bottom of your screen"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Make sure that you swipe up from the bottom edge of the screen."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Try holding the window for longer before releasing."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Make sure that you swipe straight up, then pause."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"You\'ve learned how to use gestures. To turn off gestures, go to Settings."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"You completed the switch apps gesture"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"You completed the switch apps gesture."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swipe to switch apps"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"To switch between apps, swipe up from the bottom of your screen, hold, then release."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"To switch between apps, swipe up with two fingers from the bottom of your screen, hold, then release."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Switch apps"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Swipe up from the bottom of your screen, hold, then release"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Well done!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"All set"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Done"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Settings"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar shown"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar hidden"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taskbar divider"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Show # more app.}other{Show # more apps.}}"</string> diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml index 54b746bc14..c8f344edc4 100644 --- a/quickstep/res/values-en-rXC/strings.xml +++ b/quickstep/res/values-en-rXC/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App suggestions enabled"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App suggestions are disabled"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicted app: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotate your device"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Please rotate your device to complete the gesture navigation tutorial"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Make sure you swipe from the far-right or far-left edge"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Make sure you swipe from the right or left edge to the middle of the screen and let go"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Make sure you swipe from the far-right or far-left edge."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Make sure you swipe from the right or left edge to the middle of the screen and let go."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"You learned how to swipe from the right to go back. Next up, learn how to switch apps."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"You completed the go back gesture"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Make sure you don\'t swipe too close to the bottom of the screen"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"You completed the go back gesture."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Make sure you don\'t swipe too close to the bottom of the screen."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"To change the sensitivity of the back gesture, go to Settings"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Swipe to go back"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"To go back to the last screen, swipe from the left or right edge to the middle of the screen."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"To go back to the last screen, swipe with 2 fingers from the left or right edge to the middle of the screen."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Go back"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Swipe from the left or right edge to the middle of the screen"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Make sure you swipe up from the bottom edge of the screen"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Make sure you don\'t pause before letting go"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Make sure you swipe straight up"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"You completed the go home gesture. Next up, learn how to go back."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"You completed the go home gesture"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Make sure you swipe up from the bottom edge of the screen."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Make sure you don\'t pause before letting go."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Make sure you swipe straight up."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"You completed the go Home gesture. Next up, learn how to go back."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"You completed the go Home gesture."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Swipe to go home"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swipe up from the bottom of your screen. This gesture always takes you to the Home screen."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Swipe up with 2 fingers from the bottom of the screen. This gesture always takes you to the Home screen."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Go home"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Swipe up from the bottom of your screen"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Great job!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Make sure you swipe up from the bottom edge of the screen"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Try holding the window for longer before releasing"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Make sure you swipe straight up, then pause"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"To go to your home screen at any time, swipe up from the bottom of your screen"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Make sure you swipe up from the bottom edge of the screen."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Try holding the window for longer before releasing."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Make sure you swipe straight up, then pause."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"You learned how to use gestures. To turn off gestures, go to Settings."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"You completed the switch apps gesture"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"You completed the switch apps gesture."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swipe to switch apps"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"To switch between apps, swipe up from the bottom of your screen, hold, then release."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"To switch between apps, swipe up with 2 fingers from the bottom of your screen, hold, then release."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Switch apps"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Swipe up from the bottom of your screen, hold, then release"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Well done!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"All set"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Done"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Settings"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar shown"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar hidden"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taskbar Divider"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Show # more app.}other{Show # more apps.}}"</string> diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml index 5dc9271c7d..eb2a2960e5 100644 --- a/quickstep/res/values-es-rUS/strings.xml +++ b/quickstep/res/values-es-rUS/strings.xml @@ -23,7 +23,7 @@ <string name="recent_task_option_freeform" msgid="48863056265284071">"Formato libre"</string> <string name="recents_empty_message" msgid="7040467240571714191">"No hay elementos recientes"</string> <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuración de uso de la app"</string> - <string name="recents_clear_all" msgid="5328176793634888831">"Cerrar todo"</string> + <string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string> <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recientes"</string> <string name="task_view_closed" msgid="9170038230110856166">"Se cerró la tarea"</string> <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string> @@ -44,45 +44,39 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugerencias de apps habilitadas"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Las sugerencias de aplicaciones están inhabilitadas"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicción de app: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rota el dispositivo"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rota el dispositivo para completar el instructivo de la navegación por gestos"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Asegúrate de deslizar desde el extremo derecho o izquierdo"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Recuerda deslizar desde el borde izquierdo o derecho hacia el centro de la pantalla y, luego, soltar"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Asegúrate de deslizar desde el extremo derecho o izquierdo."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Asegúrate de deslizar desde borde izquierdo o derecho hacia centro de la pantalla y, luego, soltar."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Ya sabes deslizar el dedo desde la derecha para ir atrás. Ahora, descubre cómo cambiar de app."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Completaste el gesto para ir atrás"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Asegúrate de no deslizar muy cerca de la parte inferior de la pantalla"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Completaste el gesto \"Atrás\"."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Asegúrate de no hacerlo muy cerca de la parte inferior de la pantalla."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Cambia sensibilidad de gesto \"Atrás\" en Configuración"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Desliza para ir atrás"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Desliza el dedo desde el borde derecho o izquierdo hasta el centro para volver a la última pantalla."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Para volver la pantalla anterior, desliza 2 dedos desde el borde izquierdo o derecho hacia el centro de la pantalla."</string> - <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Volver atrás"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Desliza desde el borde izquierdo o derecho hacia el centro de la pantalla"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Asegúrate de deslizar hacia arriba desde el borde inferior de la pantalla"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Asegúrate de no detenerte antes de soltar"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Asegúrate de deslizar el dedo directamente hacia arriba"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Completaste el gesto para ir a la página principal. A continuación, obtén información para volver."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Completaste el gesto para ir a la página principal"</string> + <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Atrás"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Desliza el dedo hacia arriba desde el borde inferior de la pantalla."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Asegúrate de no detenerte antes de soltarlo."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Desliza el dedo directamente hacia arriba."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Completaste el gesto para ir a la página principal. Ahora, descubre cómo ir hacia atrás."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Completaste el gesto para ir a la página principal."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Desliza para ir a la página principal"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Desliza hacia arriba desde la parte inferior de la pantalla. Este gesto te llevará a la pantalla principal."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Desliza hacia arriba desde la parte inferior. Este gesto te llevará siempre a la pantalla principal."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Ve a la pantalla principal"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Desliza hacia arriba desde la parte inferior de la pantalla"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"¡Bien hecho!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Asegúrate de deslizar hacia arriba desde el borde inferior de la pantalla"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Intenta mantener presionada la ventana más tiempo antes de soltarla"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Asegúrate de deslizar directamente hacia arriba y detenerte"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Ir a la pantalla principal"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Para ir a la pantalla principal en cualquier momento, desliza desde la parte inferior de la pantalla"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Desliza el dedo hacia arriba desde el borde inferior de la pantalla."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Prueba mantener presionada la ventana más tiempo antes de soltarla."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Desliza el dedo directamente hacia arriba y luego detente."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Ya sabes cómo usar los gestos. Para desactivarlos, ve a Configuración."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Completaste el gesto para cambiar de app"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Completaste el gesto para cambiar de app."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Desliza para cambiar de app"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Desliza el dedo hacia arriba desde la parte inferior, mantenlo presionado y suéltalo."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Para alternar entre apps, desliza el dedo hacia arriba, mantén presionado y, luego, suelta."</string> - <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Cambia de app"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Desliza hacia arriba desde la parte inferior de la pantalla, mantenla presionada y, luego, suelta"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"¡Bien hecho!"</string> + <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Cambiar de app"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Listo"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Listo"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Configuración"</string> - <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Vuelve a intentarlo"</string> + <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Reintentar"</string> <string name="gesture_tutorial_nice" msgid="2936275692616928280">"¡Genial!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"Instructivo <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"Todo listo"</string> @@ -107,7 +101,7 @@ <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Desliza despacio hacia arriba para ver la Barra de tareas"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Recibe sugerencias de aplicaciones basadas en tu rutina"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Activa la navegación por gestos en la Configuración y la Barra de tareas se ocultará sola"</string> - <string name="taskbar_edu_features" msgid="3320337287472848162">"Aprovecha mejor la Barra de tareas"</string> + <string name="taskbar_edu_features" msgid="3320337287472848162">"Haz más con la Barra de tareas"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Cerrar"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Listo"</string> <string name="taskbar_button_home" msgid="2151398979630664652">"Botón de inicio"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barra de tareas visible"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barra de tareas oculta"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegación"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Ver siempre la Barra de tareas"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Cambiar el modo de navegación"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divisor de la Barra de tareas"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover a la parte superior o izquierda"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover a la parte inferior o derecha"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Mostrar # app más.}other{Mostrar # apps más.}}"</string> diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml index ca8cbeec24..d8beb80126 100644 --- a/quickstep/res/values-es/strings.xml +++ b/quickstep/res/values-es/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugerencias de aplicaciones habilitadas"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Las sugerencias de aplicaciones están inhabilitadas"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplicación sugerida: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Gira el dispositivo"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Gira el dispositivo para completar el tutorial de navegación por gestos"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Asegúrate de deslizar desde el borde derecho o izquierdo de la pantalla"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Asegúrate de deslizar desde el borde derecho o izquierdo de la pantalla hasta el centro y soltar"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Asegúrate de deslizar el dedo desde el borde derecho o izquierdo de la pantalla."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Asegúrate de deslizar el dedo desde el borde derecho o izquierdo de la pantalla hasta el centro y luego levántalo."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Ya sabes deslizar el dedo desde la derecha para ir atrás. Descubre ahora cómo cambiar de aplicación."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Has completado el gesto para volver"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"No deslices demasiado cerca de la parte inferior de la pantalla"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Has completado el gesto para volver."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"No deslices el dedo demasiado cerca de la parte inferior de la pantalla."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Para cambiar la sensibilidad del gesto, ve a Ajustes"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Desliza para ir atrás"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Para volver a la última pantalla, desliza el dedo desde un lateral de la pantalla hasta el centro."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Para volver a la pantalla anterior, desliza dos dedos desde el borde izquierdo o derecho hacia el centro de la pantalla."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Volver"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Desliza desde el borde izquierdo o derecho de la pantalla hasta el centro"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Asegúrate de deslizar hacia arriba desde el borde inferior de la pantalla"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Asegúrate de no hacer ninguna pausa antes de soltar"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Asegúrate de deslizar hacia arriba"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Has completado el gesto para ir a la pantalla de inicio. Ahora, descubre cómo volver."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Has completado el gesto para ir a la pantalla de inicio"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Asegúrate de deslizar el dedo hacia arriba desde el borde inferior de la pantalla."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"No hagas ninguna pausa antes de levantar el dedo."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Asegúrate de deslizar el dedo hacia arriba."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Has completado el gesto para ir a la pantalla de inicio. Ahora, descubre cómo ir hacia atrás."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Has completado el gesto para ir a la pantalla de inicio."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Desliza para ir a la pantalla de inicio"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Desliza hacia arriba desde la parte inferior de la pantalla. Este gesto siempre te lleva a la pantalla de inicio."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Desliza dos dedos hacia arriba desde la parte inferior de la pantalla. Si haces este gesto, siempre irás a la pantalla de inicio."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Ir a inicio"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Desliza hacia arriba desde la parte inferior de la pantalla"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"¡Bien hecho!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Asegúrate de deslizar hacia arriba desde el borde inferior de la pantalla"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Prueba a mantener pulsada la ventana durante más tiempo antes de soltarla"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Asegúrate de deslizar directamente hacia arriba y luego detenerte"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Ir a Inicio"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Para ir a la pantalla de inicio, desliza el dedo hacia arriba desde la parte inferior de la pantalla"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Asegúrate de deslizar el dedo hacia arriba desde el borde inferior de la pantalla."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Prueba a mantener pulsada la ventana durante más tiempo antes de soltarla."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Asegúrate de deslizar el dedo directamente hacia arriba y luego mantenlo pulsado."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Ya sabes cómo utilizar gestos. Para desactivarlos, ve a Ajustes."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Has completado el gesto para cambiar de aplicación"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Has completado el gesto para cambiar de aplicación."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Desliza el dedo para cambiar de aplicación"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Para cambiar de aplicación, desliza el dedo hacia arriba desde el borde inferior, mantenlo pulsado y suéltalo."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Para cambiar de aplicación, desliza el dedo hacia arriba desde el borde inferior, mantenlo pulsado y suéltalo"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Para cambiar de app, desliza dos dedos hacia arriba desde el borde inferior, mantén pulsada la pantalla y, luego, suelta."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Cambiar de aplicación"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Desliza hacia arriba desde la parte inferior de la pantalla, mantenla pulsada y suelta el dedo."</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"¡Muy bien!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Todo listo"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Hecho"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Ajustes"</string> @@ -94,7 +88,7 @@ <string name="action_share" msgid="2648470652637092375">"Compartir"</string> <string name="action_screenshot" msgid="8171125848358142917">"Hacer captura"</string> <string name="action_split" msgid="2098009717623550676">"Dividir"</string> - <string name="toast_split_select_app" msgid="8464310533320556058">"Toca otra aplicación para usar la pantalla dividida"</string> + <string name="toast_split_select_app" msgid="8464310533320556058">"Toca otra app para usar la pantalla dividida"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Elige otra app para usar la pantalla dividida"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"No puedes hacerlo porque la aplicación o tu organización no lo permiten"</string> <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"¿Saltar tutorial de navegación?"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barra de tareas visible"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barra de tareas oculta"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegación"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Ver siempre Barra de Tareas"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Cambiar el modo de navegación"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divisor de Barra de Tareas"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover arriba/a la izquierda"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover abajo/a la derecha"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Mostrar # aplicación más.}other{Mostrar # aplicaciones más.}}"</string> diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml index 0297d730d1..15943379dc 100644 --- a/quickstep/res/values-et/strings.xml +++ b/quickstep/res/values-et/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Rakenduste soovitused on lubatud"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Rakenduste soovitused on keelatud"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Ennustatud rakendus: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Pöörake seadet"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Pöörake seadet, et liigutustega navigeerimise õpetused lõpetada"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Pühkige kindlasti parem- või vasakpoolsest servast"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Pühkige ekraani paremast või vasakust servast keskele ja eemaldage sõrm"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Pühkige kindlasti parem- või vasakpoolsest servast."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Pühkige ekraanikuva paremast või vasakust servast keskele ja eemaldage sõrm."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Õppisite, kuidas tagasiliikumiseks paremalt pühkida. Nüüd vaadake, kuidas rakenduste vahel vahetada."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Tegite tagasiliikumise liigutuse"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Veenduge, et te ei pühiks liiga ekraani allosa lähedalt."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Tegite tagasiliikumise liigutuse."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Veenduge, et te ei pühiks liiga ekraanikuva allosa lähedalt."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Tagasiliigutuse tundlikkuse muutmiseks avage menüü Seaded"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Tagasiliikumiseks pühkige"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Eelmisele ekraanikuvale naasmiseks pühkige vasakust või paremast servast ekraanikuva keskele."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Eelmisele ekraanikuvale naasmiseks pühkige vasakust või paremast servast kahe sõrmega ekraanikuva keskele."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Tagasiliikumine"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Pühkige ekraani paremast või vasakust servast keskele."</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Pühkige kindlasti ekraani alumisest servast üles."</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Veenduge, et te enne vabastamist liigutust ei peataks."</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Pühkige kindlasti otse üles."</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Tegite avakuvale minemise liigutuse. Järgmisena vaadake, kuidas minna tagasi."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Tegite avakuvale minemise liigutuse"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Pühkige kindlasti ekraanikuva alumisest servast üles."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Veenduge, et te enne vabastamist liigutust ei peataks."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Pühkige kindlasti otse üles."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Tegite avakuvale minemise liigutuse. Järgmisena vaadake, kuidas minna tagasi."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Tegite avakuvale minemise liigutuse."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Pühkige avakuvale minemiseks"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Pühkige ekraani alaosast üles. See liigutus viib teid alati tagasi avakuvale."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Pühkige ekraanikuva alumisest servast 2 sõrmega üles. See liigutus viib teid alati tagasi avakuvale."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Avakuvale"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Pühkige ekraani allosast üles."</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Väga hea!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Pühkige kindlasti ekraani alumisest servast üles."</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Hoidke sõrme aknal pisut kauem, enne kui vabastate"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Pühkige kindlasti otse üles, seejärel peatuge"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Avalehele"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Mis tahes ajal avakuvale liikumiseks pühkige ekraanikuva allosast üles"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Pühkige kindlasti ekraanikuva alumisest servast üles."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Hoidke sõrme aknal pisut kauem, enne kui vabastate."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Pühkige kindlasti otse üles, seejärel peatuge."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Õppisite liigutusi kasutama. Liigutuste väljalülitamiseks avage seaded."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Tegite rakenduste vahel vahetamise liigutuse"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Tegite rakenduste vahel vahetamise liigutuse."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Pühkige rakenduste vahetamiseks"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Rakenduste vahel vahetamiseks pühkige ekraanikuva alaosast üles, hoidke ja seejärel vabastage."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Rakenduste vahel vahetamiseks pühkige kuva alaosast kahe sõrmega üles, hoidke ja seejärel vabastage."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Rakenduste vahetamine"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Pühkige ekraani allosast üles, hoidke ja seejärel vabastage."</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Hästi tehtud!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Valmis"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Valmis"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Seaded"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Tegumiriba on kuvatud"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Tegumiriba on peidetud"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigeerimisriba"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Kuva tööriistariba alati"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Navigeerimisrežiimi muutmine"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Tegumiriba jagaja"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Teisalda üles/vasakule"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Teisalda alla/paremale"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Kuva veel # rakendus.}other{Kuva veel # rakendust.}}"</string> diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml index 7d18d32eae..1eba2b7eeb 100644 --- a/quickstep/res/values-eu/strings.xml +++ b/quickstep/res/values-eu/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Gaituta daude aplikazioen iradokizunak"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Desgaituta daude aplikazioen iradokizunak"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Iragarritako aplikazioa: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Biratu gailua"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Keinu bidezko nabigazioaren tutoriala osatzeko, biratu gailua"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Ziurtatu hatza pantailaren eskuineko edo ezkerreko ertzetik hasten zarela pasatzen"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Ziurtatu hatza pantailaren eskuineko edo ezkerreko ertzetik erdialdera pasatzen duzula eta ondoren hatza jasotzen duzula"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Ziurtatu hatza pantailaren eskuineko edo ezkerreko ertzetik hasten zarela pasatzen."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Ziurtatu hatza pantailaren eskuineko edo ezkerreko ertzetik erdialdera pasatzen eta altxatzen duzula."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Hatza eskuinetik pasatuta atzera egiten ikasi duzu. Jarraian, ikasi aplikazioa aldatzen."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Ikasi duzu atzera egiteko keinua"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Ziurtatu hatza ez duzula pasatzen pantailaren behealdetik gertuegi"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Ikasi duzu atzera egiteko keinua."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Ziurtatu hatza ez duzula pantailaren behealdetik gertuegi pasatzen."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Keinuaren sentikortasuna aldatzeko, joan ezarpenetara"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Pasatu hatza atzera egiteko"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Aurreko pantailara itzultzeko, pasatu hatza pantailaren ezkerreko edo eskuineko ertzetik erdialdera."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Aurreko pantailara itzultzeko, pasatu bi hatz pantailaren ezkerreko edo eskuineko ertzetik erdialdera."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Egin atzera"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Pasatu hatza pantailaren eskuineko edo ezkerreko ertzetik erdialdera"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Ziurtatu hatza pantailaren beheko ertzetik gora pasatzen duzula"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Ziurtatu ez duzula mugimendua gelditzen askatu arte"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Ziurtatu hatza zuzen pasatzen duzula gora"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Ikasi duzu hasierako pantailara joateko keinua. Orain, ikasi atzera egiten."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Ikasi duzu hasierako pantailara joateko keinua"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Ziurtatu hatza pantailaren beheko ertzetik gora pasatzen duzula."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Ziurtatu askatu aurretik ez duzula hatza gelditzen."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Ziurtatu hatza zuzen pasatzen duzula gora."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Ikasi duzu hasierako pantailara joateko keinua. Jarraian, ikasi atzera egiten."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Ikasi duzu hasierako pantailara joateko keinua."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Pasatu hatza hasierako pantailara joateko"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Pasatu hatza pantailaren behealdetik gora. Keinu horrek hasierako pantailara eramango zaitu beti."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Pasatu bi hatz pantailaren behealdetik gora. Hasierako pantailara eramango zaitu beti keinu horrek."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Joan hasierako pantailara"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Pasatu hatza pantailaren behealdetik gora"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Bikain!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Ziurtatu hatza pantailaren beheko ertzetik gora pasatzen duzula"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Eduki sakatuta leihoa luzaroago hatza jaso aurretik"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Ziurtatu hatza zuzen pasatzen duzula gora; ondoren, gelditu"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Hasierako pantailara joateko, pasatu hatza pantailaren behealdetik gora"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Ziurtatu hatza pantailaren beheko ertzetik gora pasatzen duzula."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Eduki sakatuta leihoa luzaroago hatza altxatu aurretik."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Ziurtatu hatza zuzen pasatzen duzula gora; ondoren, gelditu."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Ikasi duzu keinuak erabiltzen. Keinuak desaktibatzeko, joan ezarpenetara."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Ikasi duzu aplikazioz aldatzeko keinua"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Ikasi duzu aplikazioa aldatzeko keinua."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Pasatu hatza aplikazioa aldatzeko"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Aplikazio batetik bestera joateko, pasatu hatza pantailaren behealdetik gora, eduki pantaila sakatuta eta altxatu hatza."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Aplikazio batetik bestera joateko, pasatu bi hatz pantailaren behealdetik gora, eduki pantaila sakatuta eta altxatu hatza."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Aldatu aplikazioa"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Pasatu hatza pantailaren behealdetik gora, eduki sakatuta une batez, eta jaso hatza"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Oso ongi!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Dena prest"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Eginda"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Ezarpenak"</string> @@ -104,7 +98,7 @@ <string name="accessibility_rotate_button" msgid="4771825231336502943">"Biratu pantaila"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Zereginen barra erabiltzeko argibideak"</string> <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Bi aplikazio batera erabiltzeko, arrastatu bat albo batera"</string> - <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Zereginen barra ikusteko, pasatu hatza gora poliki"</string> + <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Zereginen barra ikusteko, pasatu hatza motel gora"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Jaso aplikazioen iradokizunak erabileran oinarrituta"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Zereginen barra automatikoki ezkutatzeko, aktibatu keinu bidezko nabigazioa ezarpenetan"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Egin gauza gehiago zereginen barrarekin"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Zereginen barra ikusgai dago"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Zereginen barra itxita dago"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Nabigazio-barra"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Erakutsi beti zereginen barra"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Aldatu nabigazio modua"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Zereginen barraren zatitzailea"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Eraman gora, ezkerretara"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Eraman behera, eskuinetara"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Erakutsi beste # aplikazio.}other{Erakutsi beste # aplikazio.}}"</string> diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml index b5545ef17b..1a35f9c94e 100644 --- a/quickstep/res/values-fa/strings.xml +++ b/quickstep/res/values-fa/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"«پیشنهاد برنامه» فعال است"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"«پیشنهاد برنامه» غیرفعال است"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"برنامه پیشبینیشده: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"دستگاهتان را بچرخانید"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"لطفاً برای تکمیل آموزش گامبهگام پیمایش اشارهای، دستگاهتان را بچرخانید"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"دقت کنید که از انتهای لبه سمت راست یا سمت چپ تند بکشید"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"دقت کنید که از لبه سمت راست یا سمت چپ تند به وسط صفحه بکشید و رها کنید"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"دقت کنید که از انتهای لبه سمت راست یا سمت چپ تند بکشید."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"دقت کنید که از لبه سمت راست یا سمت چپ تند به وسط صفحه بکشید و رها کنید."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"یاد گرفتید چگونه برای رفتن به عقب از سمت راست تند بکشید. مورد بعدی، با نحوه جابهجا شدن بین برنامهها آشنا شوید."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"اشاره برگشتن را تکمیل کردید"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"دقت کنید که موقع تند کشیدن، بیشاز حد به پایین صفحه نزدیک نشوید"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"اشاره برگشتن را تکمیل کردید."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"دقت کنید که موقع تند کشیدن بیشاز حد به پایین صفحه نزدیک نشوید."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"برای تغییر حساسیت اشاره برگشت، به «تنظیمات» بروید"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"تند بکشید تا بهعقب برگردید"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"برای برگشتن به صفحه آخر، از لبه سمت چپ یا راست تند به وسط صفحه بکشید."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"برای برگشتن به صفحه قبلی، با ۲ انگشت از لبه سمت چپ یا راست تند بهوسط صفحه بکشید."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"برگشتن"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"از لبه سمت راست یا سمت چپ تند به وسط صفحه بکشید"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"دقت کنید که از لبه پایینی صفحه تند به بالا بکشید"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"دقت کنید که قبلاز رها کردن مکث نکنید"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"دقت کنید که مستقیماً تند به بالا بکشید"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"اشاره رفتن به صفحه اصلی را تکمیل کردید. مورد بعدی، با نحوه برگشتن به عقب آشنا شوید."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"اشاره رفتن به صفحه اصلی را تکمیل کردید"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"دقت کنید که از لبه پایینی صفحه تند به بالا بکشید."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"دقت کنید که تا قبلاز رها کردن، کشیدن را متوقف نکنید."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"دقت کنید که مستقیماً تند به بالا بکشید."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"اشاره رفتن به «صفحه اصلی» را تکمیل کردید. مورد بعدی، با نحوه برگشتن به عقب آشنا شوید."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"اشاره رفتن به «صفحه اصلی» را تکمیل کردید."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"تند کشیدن برای رفتن به صفحه اصلی"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"از پایین صفحه، تند بهسمت بالا بکشید. این اشاره همیشه شما را به صفحه اصلی میبرد."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"با ۲ انگشت از پایین صفحه تند بهبالا بکشید. این اشاره همیشه شما را به صفحه اصلی میبرد."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"رفتن به صفحه اصلی"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"از پایین صفحه تند به بالا بکشید"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"عالی است!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"دقت کنید که از لبه پایینی صفحه تند به بالا بکشید"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"قبلاز رها کردن پنجره، آن را برای مدت طولانیتری نگه دارید"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"دقت کنید که مستقیماً تند به بالا بکشید و سپس توقف کنید"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"برای رفتن به صفحه اصلی در هر زمانی، از پایین صفحه تند بهبالا بکشید"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"دقت کنید که از لبه پایینی صفحه تند به بالا بکشید."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"سعی کنید قبلاز رها کردن، پنجره را برای مدت طولانیتری نگه دارید."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"دقت کنید که مستقیماً تند به بالا بکشید و سپس توقف کنید."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"با نحوه استفاده از اشارهها آشنا شدید. برای خاموش کردن اشارهها، به «تنظیمات» بروید."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"اشاره جابهجایی بین برنامهها را تکمیل کردید"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"اشاره جابهجا شدن بین برنامهها را تکمیل کردید."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"برای جابهجا شدن بین برنامهها، تند بهبالا بکشید"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"برای جابهجا شدن بین برنامهها، از پایین صفحه تند بهبالا بکشید، نگه دارید، و سپس رها کنید."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"برای جابهجایی بین برنامهها، با ۲ انگشت از پایین صفحه تند بهبالا بکشید، نگه دارید، و سپس رها کنید."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"جابهجایی بین برنامهها"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"از پایین صفحه تند بهبالا بکشید، نگه دارید، سپس رها کنید"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"آفرین!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"همه چیز آماده است"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"تمام"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"تنظیمات"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"نوار وظیفه نمایان است"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"نوار وظیفه پنهان است"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"نوار پیمایش"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"نوار وظیفه همیشه نشان داده شود"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"تغییر حالت پیمایش"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"جداکننده نوار وظیفه"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"انتقال به بالا/ چپ"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"انتقال به پایین/ راست"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{نمایش # برنامه دیگر.}one{نمایش # برنامه دیگر.}other{نمایش # برنامه دیگر.}}"</string> diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml index 748a24547f..428011b180 100644 --- a/quickstep/res/values-fi/strings.xml +++ b/quickstep/res/values-fi/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sovellusehdotukset käytössä"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sovellusehdotukset on poistettu käytöstä"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Ennakoitu sovellus: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Käännä laite"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Käännä laite, niin voit katsoa esittelyn eleillä navigoinnista"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Pyyhkäise aivan oikeasta tai vasemmasta reunasta"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Pyyhkäise näytön oikeasta tai vasemmasta reunasta keskelle ja päästä irti"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Muista pyyhkäistä aivan oikeasta tai vasemmasta reunasta."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Pyyhkäise näytön oikeasta tai vasemmasta reunasta keskelle ja päästä irti."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Osaat palata takaisin pyyhkäisemällä oikeasta reunasta. Opettele seuraavaksi vaihtamaan sovellusta."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Olet oppinut takaisin-eleen"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Varo, ettet pyyhkäise liian lähellä alareunaa"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Olet oppinut Takaisin-eleen."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Varo, ettet pyyhkäise liian lähellä alareunaa."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Voit muuttaa Takaisin-eleen herkkyyttä asetuksista"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Siirry takaisin pyyhkäisemällä"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Voit palata edelliseen näkymään pyyhkäisemällä näytön vasemmasta tai oikeasta reunasta keskelle."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Palaa takaisin edelliselle näytölle pyyhkäisemällä kahdella sormella vasemmasta tai oikeasta reunasta näytön keskikohtaan."</string> - <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Edelliseen siirtyminen"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Pyyhkäise vasemmasta tai oikeasta reunasta näytön keskelle"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Pyyhkäise ylös näytön alareunasta"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Varo pysähtymästä ennen kuin päästät irti"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Pyyhkäise suoraan ylöspäin"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Olet oppinut aloitusnäytölle palaamiseleen. Opettele seuraavaksi siirtymään takaisin."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Olet oppinut aloitusnäytölle palaamiseleen"</string> + <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Takaisin"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Pyyhkäise ylös näytön alareunasta."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Varo keskeyttämästä ennen kuin päästät irti."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Muista pyyhkäistä suoraan ylöspäin."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Olet oppinut aloitusnäytölle palaamisen eleen. Opettele seuraavaksi siirtymään takaisin."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Olet oppinut aloitusnäytölle palaamisen eleen."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Siirry aloitusnäytölle pyyhkäisemällä"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Pyyhkäise ylös näytön alareunasta. Tämä ele vie sinut aina aloitusnäytölle."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Pyyhkäise näytön alareunasta ylöspäin kahdella sormella. Tämä ele vie sinut aina aloitusnäytölle."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Aloitusnäytölle siirtyminen"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Pyyhkäise ylös näytön alareunasta"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Hienoa!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Pyyhkäise ylös näytön alareunasta"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Kokeile pitää ikkunaa painettuna pidempään ennen kuin päästät irti"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Muista pyyhkäistä suoraan ylöspäin ja pysähdy sitten"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Voit siirtyä aloitusnäytölle milloin tahansa pyyhkäisemällä ylös näytön alareunasta"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Pyyhkäise ylös näytön alareunasta."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Kokeile pitää ikkunaa painettuna pidempään ennen kuin päästät irti."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Muista pyyhkäistä suoraan ylöspäin ja keskeytä sitten."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Olet oppinut käyttämään eleitä. Jos haluat laittaa eleet pois päältä, avaa Asetukset."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Olet oppinut sovellusten vaihtamiseleen"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Olet oppinut sovellusten vaihtamiseleen."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Vaihda sovellusta pyyhkäisemällä"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Voit vaihtaa sovelluksesta toiseen pyyhkäisemällä ylöspäin näytön alareunasta ja päästämällä sitten irti."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Vaihda sovelluksia pyyhkäisemällä ylös näytön alareunasta kahdella sormella ja päästä sitten irti."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Sovelluksen vaihtaminen"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Pyyhkäise ylöspäin näytön alareunasta ja päästä irti"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Hienoa!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Valmista"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Valmis"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Asetukset"</string> @@ -103,7 +97,7 @@ <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ohita"</string> <string name="accessibility_rotate_button" msgid="4771825231336502943">"Käännä näyttö"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Tehtäväpalkin ohje"</string> - <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Vedä sovellus sivuun, ja voit käyttää kahta sovellusta"</string> + <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Vedä sovellus sivuun, niin voit käyttää samalla 2 sellaista"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Näytä tehtäväpalkki pyyhkäisemällä ylös hitaasti"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Sovellussuosituksia käytön perusteella"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Laita eleillä navigointi päälle Asetuksista Tehtäväpalkin piilottamiseksi automaattisesti"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Tehtäväpalkki näkyvissä"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Tehtäväpalkki piilotettu"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigointipalkki"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Näytä tehtäväpalkki aina"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Vaihda navigointitilaa"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Tehtäväpalkin jakaja"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Siirrä ylös tai vasemmalle"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Siirrä alas tai oikealle"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Näytä # muu sovellus.}other{Näytä # muuta sovellusta.}}"</string> diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml index 4029bb201e..af89ae5c91 100644 --- a/quickstep/res/values-fr-rCA/strings.xml +++ b/quickstep/res/values-fr-rCA/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Les suggestions d\'applications sont activées"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Les suggestions d\'applications sont désactivées"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Application prédite : <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Faites pivoter votre appareil"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Veuillez faire pivoter votre appareil pour terminer le tutoriel de navigation par gestes"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Assurez-vous de balayer l\'écran à partir de l\'extrémité droite ou gauche"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Assurez-vous de balayer l\'écran à partir de l\'extrémité droite ou gauche vers le centre, puis allons-y"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Assurez-vous de balayer l\'écran à partir de l\'extrémité droite ou gauche."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Balayez l\'écran de l\'extrémité droite ou gauche jusqu\'au centre de l\'écran, puis levez le doigt."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Vous avez appris à balayer de la droite pour revenir en arrière. Apprenez comment changer d\'appli."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Vous avez appris le geste de retour en arrière"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Assurez-vous de ne pas balayer trop près du bas de l\'écran"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Vous avez appris le geste de retour en arrière."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Assurez-vous de ne pas balayer trop près du bas de l\'écran."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Modifiez la sensibilité du geste de retour dans Paramètres"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Balayez l\'écran pour revenir en arrière"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Pour revenir à l\'écran précédent, balayez l\'écran de l\'extrémité gauche ou droite vers le centre."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Pour revenir à l\'écran précédent, balayez l\'écran avec deux doigts du bord gauche ou droit jusqu\'au centre."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Retour"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Balayez l\'écran à partir du bord gauche ou droit de l\'écran vers le centre"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Assurez-vous de balayer l\'écran à partir de l\'extrémité inférieure vers le haut"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Assurez-vous de ne pas interrompre le geste avant de lever le doigt"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Assurez-vous de balayer l\'écran en ligne droite vers le haut"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Vous avez appris le geste de retour à l\'écran d\'accueil. Maintenant, apprenez à revenir en arrière."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Vous avez appris le geste de retour à l\'écran d\'accueil"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Assurez-vous de balayer l\'écran à partir de l\'extrémité inférieure vers le haut."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Assurez-vous de ne pas interrompre le geste avant de lever le doigt."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Assurez-vous de balayer l\'écran en ligne droite vers le haut."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Vous avez appris le geste de retour à l\'écran d\'accueil. Maintenant, apprenez à revenir en arrière."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Vous avez appris le geste de retour à l\'écran d\'accueil."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Balayez pour revenir à l\'écran d\'accueil"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Balayez l\'écran du bas vers le haut. Ce geste vous ramène toujours à l\'écran d\'accueil."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Balayez l\'écran de bas en haut avec deux doigts. Ce geste vous ramène toujours à l\'écran d\'accueil."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Retour à la page d\'accueil"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Balayez votre écran du bas vers le haut"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Super!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Assurez-vous de balayer l\'écran à partir de l\'extrémité inférieure vers le haut"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Essayez de tenir la fenêtre plus longtemps avant de relâcher"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Assurez-vous de balayer l\'écran vers le haut, puis de faire une pause"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Pour accéder à votre écran d\'accueil à tout moment, balayez l\'écran du bas vers le haut"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Assurez-vous de balayer l\'écran à partir de l\'extrémité inférieure vers le haut."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Essayez de tenir la fenêtre plus longtemps avant de relâcher."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Assurez-vous de balayer l\'écran en ligne droite vers le haut, puis de faire une pause."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Vous avez appris à utiliser les gestes. Pour les désactiver, accédez au menu Paramètres."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Vous avez appris le geste de changement d\'application"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Vous avez appris le geste de changement d\'application."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Balayez pour basculer entre les applications"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Pour changer d\'application, balayez l\'écran de bas en haut, maintenez le doigt dessus, puis relâchez-le."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Pour changer d\'application, balayez l\'écran de bas en haut, maintenez le doigt, puis relâchez-le."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Pour changer d\'appli, balayez l\'écran de bas en haut avec deux doigts, maintenez-les et relâchez-les."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Changer d\'application"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Balayez l\'écran de bas en haut, maintenez le doigt en place, puis relâchez-le"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Bien joué!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Terminé"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"OK"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Paramètres"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barre des tâches affichée"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barre des tâches masquée"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barre de navigation"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Touj. afficher barre des tâches"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Changer de mode de navigation"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Séparateur de la barre des tâches"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer vers le coin supérieur gauche de l\'écran"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer vers le coin inférieur droit de l\'écran"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Afficher # autre application.}one{Afficher # autre application.}other{Afficher # autres applications.}}"</string> diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml index b86c18f0e7..3b08b8969c 100644 --- a/quickstep/res/values-fr/strings.xml +++ b/quickstep/res/values-fr/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Suggestions d\'applications activées"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Les suggestions d\'applications sont désactivées"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Application prédite : <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Faire pivoter l\'appareil"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Veuillez faire pivoter votre appareil pour effectuer le tutoriel de navigation par gestes"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Veillez à bien balayer l\'écran depuis le bord gauche ou droit"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Balayez bien l\'écran depuis le bord gauche ou droit jusqu\'au centre avant de relever le doigt"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Veillez à bien balayer l\'écran depuis le bord gauche ou droit."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Balayez bien l\'écran depuis le bord gauche ou droit jusqu\'au centre avant de relever le doigt."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Vous savez revenir en arrière en balayant depuis la droite. Apprenez à passer d\'une appli à l\'autre."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Vous avez appris le geste pour revenir en arrière"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Veillez à ne pas balayer l\'écran trop près du bas"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Vous avez appris le geste pour revenir en arrière."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Veillez à ne pas balayer l\'écran trop près du bas."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Modifiez la sensibilité du geste retour dans les paramètres"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Balayez l\'écran pour revenir en arrière"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Pour revenir à l\'écran précédent, balayez l\'écran depuis le bord droit ou gauche jusqu\'au centre."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Pour revenir au dernier écran, balayez l\'écran avec deux doigts en partant du bord gauche ou droit vers le milieu."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Retour"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Balayez l\'écran à partir du bord gauche ou droit jusqu\'au centre"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Veillez à balayer vers le haut depuis le bord inférieur de l\'écran"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Veillez à ne pas marquer de pause dans votre geste avant de relever le doigt"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Veillez à balayer l\'écran vers le haut en ligne droite"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Vous avez appris le geste pour revenir à l\'écran d\'accueil. Apprenez ensuite à revenir en arrière."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Vous avez appris le geste pour revenir à l\'écran d\'accueil"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Veillez à balayer l\'écran de bas en haut."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Veillez à ne pas marquer de pause dans votre geste avant de relever le doigt."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Veillez à balayer l\'écran vers le haut."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Vous savez désormais revenir à l\'écran d\'accueil. Apprenez maintenant à revenir en arrière."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Vous avez appris le geste pour revenir à l\'écran d\'accueil."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Balayez pour revenir à l\'écran d\'accueil"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Balayez l\'écran de bas en haut. Ce geste vous ramènera toujours à l\'écran d\'accueil."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Balayez l\'écran de bas en haut avec 2 doigts. Ce geste vous ramènera toujours à l\'écran d\'accueil."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Retour à l\'accueil"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Balayez l\'écran de bas en haut"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Bravo !"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Veillez à balayer vers le haut depuis le bord inférieur de l\'écran"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Essayez d\'appuyer plus longtemps sur la fenêtre avant de relever le doigt"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Veillez à balayer l\'écran vers le haut, puis à marquer une pause"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Pour accéder à l\'écran d\'accueil à tout moment, balayez l\'écran du bas vers le haut"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Veillez à balayer l\'écran de bas en haut."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Essayez d\'appuyer plus longtemps sur la fenêtre avant de relever le doigt."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Veillez à balayer l\'écran vers le haut, puis à marquer une pause."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Vous avez appris à utiliser les gestes. Pour les désactiver, accédez aux paramètres."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Vous avez appris le geste pour passer d\'une appli à l\'autre"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Vous avez appris le geste pour passer d\'une appli à l\'autre."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Balayez pour passer d\'une appli à l\'autre"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Pour changer d\'appli, balayez l\'écran de bas en haut, appuyez de manière prolongée et relâchez."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Pour changer d\'appli, balayez l\'écran de bas en haut avec deux doigts, maintenez appuyé et relâchez."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Changer d\'appli"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Balayez l\'écran de bas en haut, appuyez de manière prolongée, puis relâchez"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Bravo !"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Vous avez terminé"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"OK"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Paramètres"</string> @@ -103,8 +97,8 @@ <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Passer"</string> <string name="accessibility_rotate_button" msgid="4771825231336502943">"Faire pivoter l\'écran"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Fonctionnement de la barre des tâches"</string> - <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Faites glisser une appli sur le côté pour en utiliser 2 à la fois"</string> - <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Balayez lentement vers haut pour afficher barre des tâches"</string> + <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Faites glisser une appli sur le côté pour utiliser 2 applis"</string> + <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Balayez lentement vers le haut pour l\'afficher"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Obtenez des suggestions d\'applis basées sur vos habitudes"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Activez la navigation par gestes dans paramètres pour masquage auto de la barre des tâches"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Faites-en plus avec la barre des tâches"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barre des tâches affichée"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barre des tâches masquée"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barre de navigation"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Toujours voir barre des tâches"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Modifier le mode de navigation"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Séparateur de barre des tâches"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer en haut ou à gauche"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer en bas ou à droite"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Afficher # autre appli.}one{Afficher # autre appli.}other{Afficher # autre applis.}}"</string> diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml index 53bc4d9344..d5e7550599 100644 --- a/quickstep/res/values-gl/strings.xml +++ b/quickstep/res/values-gl/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"As suxestións de aplicacións están activadas"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"As suxestións de aplicacións están desactivadas"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplicación predita: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Xira o dispositivo"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Xira o dispositivo para completar o titorial de navegación con xestos"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Asegúrate de pasar o dedo desde o bordo dereito ou esquerdo"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Asegúrate de pasar o dedo desde o bordo dereito ou esquerdo ata o medio da pantalla e levantalo"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Asegúrate de pasar o dedo desde o bordo dereito ou esquerdo."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Asegúrate de pasar o dedo desde o bordo dereito ou esquerdo ata o medio da pantalla e avanza."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Aprendiches a pasar o dedo desde a dereita para volver. Agora, aprende a cambiar de aplicación."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Completaches o xesto de volver á última pantalla"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Asegúrate de non pasar o dedo demasiado preto da parte inferior da pantalla"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Completaches o xesto de volver á última pantalla."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Asegúrate de non pasar o dedo demasiado preto da parte inferior da pantalla."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Podes cambiar a sensibilidade do xesto en Configuración"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Pasa o dedo para volver"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Para volver á última pantalla, pasa o dedo cara ao medio desde o bordo dereito ou esquerdo."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Para volver á última pantalla, pasa 2 dedos desde o bordo esquerdo ou o dereito ata a metade da pantalla."</string> - <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Volver atrás"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Pasa o dedo desde o bordo esquerdo ou dereito ata o medio da pantalla"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Asegúrate de pasar o dedo cara arriba desde o bordo inferior da pantalla"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Asegúrate de non facer unha pausa antes de levantar o dedo"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Asegúrate de pasar o dedo cara arriba cun movemento vertical"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Completaches o xesto de ir ao inicio. O próximo é aprender a volver á última pantalla."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Completaches o xesto de ir ao inicio"</string> + <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Atrás"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Asegúrate de pasar o dedo cara arriba desde o bordo inferior da pantalla."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Asegúrate de non facer unha pausa antes de avanzar."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Asegúrate de pasar o dedo cara arriba cun movemento vertical."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Completaches o xesto de ir ao inicio. O próximo é aprender a volver á última pantalla."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Completaches o xesto de ir ao inicio."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Pasa o dedo para ir ao inicio"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Pasa o dedo cara arriba desde a parte inferior da pantalla. Ao facelo, irás á pantalla de inicio."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Pasa 2 dedos desde a parte inferior da pantalla. Ao facelo, sempre irás á pantalla de inicio."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Ir á pantalla de inicio"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Pasa o dedo cara arriba desde a parte inferior da pantalla"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Moi ben!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Asegúrate de pasar o dedo cara arriba desde o bordo inferior da pantalla"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Proba a manter premida a ventá máis tempo antes de levantar o dedo"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Asegúrate de pasar o dedo cara arriba cun movemento vertical. Despois, fai unha pausa"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Para ir á pantalla de inicio, pasa o dedo cara arriba desde a parte inferior da pantalla"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Asegúrate de pasar o dedo cara arriba desde o bordo inferior da pantalla."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Proba a manter premida a pantalla máis tempo antes de soltala."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Asegúrate de pasar o dedo cara arriba cun movemento vertical. Despois, fai unha pausa."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Aprendiches a usar os xestos. Para desactivalos, vai a Configuración."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Completaches o xesto para cambiar de aplicación"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Completaches o xesto para cambiar de aplicación."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Pasa o dedo para cambiar de aplicación"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Para cambiar de aplicación, pasa o dedo cara arriba desde a parte inferior da pantalla, mantén premido e levanta o dedo."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Para cambiar de app, pasa 2 dedos cara arriba desde abaixo, mantén premida a pantalla e levántaos."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Pasar dunha aplicación a outra"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Pasa o dedo cara arriba desde a parte inferior da pantalla, mantena premida e sepárao"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Ben feito!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Todo listo"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Feito"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Configuración"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Estase mostrando a barra de tarefas"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Non se está mostrando a barra de tarefas"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegación"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Manter Barra de tarefas"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Cambiar modo de navegación"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divisor da Barra de tarefas"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover á parte superior ou á esquerda"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover á parte inferior ou á dereita"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Mostrar # aplicación máis.}other{Mostrar # aplicacións máis.}}"</string> diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml index 519affd0e4..0924b681c8 100644 --- a/quickstep/res/values-gu/strings.xml +++ b/quickstep/res/values-gu/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ઍપના સુઝાવો ચાલુ છે"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ઍપના સુઝાવો બંધ છે"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"પૂર્વાનુમાનિત ઍપ: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"તમારા ડિવાઇસને ફેરવો"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"સંકેતથી નૅવિગેશન ટ્યૂટૉરિઅલ પૂર્ણ કરવા માટે કૃપા કરીને તમારા ડિવાઇસને ફેરવો"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ખાતરી કરો કે તમે એકદમ દૂરની જમણી કે ડાબી કિનારીએથી સ્વાઇપ કરો છો"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"ખાતરી કરો કે તમે જમણી કે ડાબી કિનારીએથી સ્ક્રીનના મધ્ય ભાગ સુધી સ્વાઇપ કરો છો અને આંગળી ઊંચકી લો છો"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"ખાતરી કરો કે તમે એકદમ દૂરની જમણી કે ડાબી કિનારીએથી સ્વાઇપ કરો છો."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"ખાતરી કરો કે તમે જમણી કે ડાબી કિનારીએથી સ્ક્રીનના મધ્ય ભાગ સુધી સ્વાઇપ કરો છો અને આંગળી ઊંચકી લો છો."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"પાછળ જવા જમણેથી કેવી રીતે સ્વાઇપ કરવું એ તમે શીખી લીધું છે. હવે પછી, ઍપ સ્વિચ કરવાની રીત જાણો."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"તમે પાછા જવાનો સંકેત પૂર્ણ કર્યો છે"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"ખાતરી કરો કે તમારાથી સ્ક્રીનની એકદમ નીચેની કિનારીની ખૂબ નજીક સુધી સ્વાઇપ ન થઈ જાય"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"તમે પાછા જવાનો સંકેત પૂર્ણ કર્યો છે."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ખાતરી કરો કે તમારાથી સ્ક્રીનની એકદમ નીચેની કિનારીની ખૂબ નજીક સુધી સ્વાઇપ ન થઈ જાય."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"પાછા જવાના સંકેતની સંવેદિતા બદલવા માટે, સેટિંગમાં જાઓ"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"પાછળ જવા માટે સ્વાઇપ કરો"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"છેલ્લી સ્ક્રીન પર પાછા જવા, ડાબી કે જમણી કિનારીએથી સ્ક્રીનના મધ્ય ભાગ સુધી સ્વાઇપ કરો."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"છેલ્લી સ્ક્રીન પર પાછા જવા માટે, 2 આંગળી વડે ડાબી કે જમણી કિનારીએથી સ્ક્રીનના મધ્ય ભાગ સુધી સ્વાઇપ કરો."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"પાછા જાઓ"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"જમણી કે ડાબી કિનારીએથી સ્ક્રીનના મધ્ય ભાગ સુધી સ્વાઇપ કરો"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"ખાતરી કરો કે તમે સ્ક્રીનની નીચેની કિનારીએથી ઉપરની તરફ સ્વાઇપ કરો છો"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"ખાતરી કરો કે તમે આંગળી ઊંચકી લેતા પહેલાં સ્વાઇપ કરવાનું થોભાવતા નથી"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"ખાતરી કરો કે તમે સીધું ઉપરની તરફ સ્વાઇપ કરો છો"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"તમે હોમ સ્ક્રીન પર જવાનો સંકેત પૂર્ણ કર્યો. હવે પછી, પાછા જવાની રીત વિશે જાણો."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"તમે હોમ સ્ક્રીન પર જવાનો સંકેત પૂર્ણ કર્યો"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ખાતરી કરો કે તમે સ્ક્રીનની નીચેની કિનારીએથી ઉપરની તરફ સ્વાઇપ કરો છો."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ખાતરી કરો કે તમે આંગળી ઊંચકી લેતા પહેલાં સ્વાઇપ કરવાનું થોભાવતા નથી."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ખાતરી કરો કે તમે સીધું ઉપરની તરફ સ્વાઇપ કરો છો."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"તમે હોમ સ્ક્રીન પર પાછા જવાનો સંકેત પૂર્ણ કર્યો છે. હવે પછી, પાછા જવાની રીત વિશે જાણો."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"તમે હોમ સ્ક્રીન પર પાછા જવાનો સંકેત પૂર્ણ કર્યો છે."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"હોમ સ્ક્રીન પર જવા માટે સ્વાઇપ કરો"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"તમારી સ્ક્રીનના નીચેના ભાગથી ઉપરની તરફ સ્વાઇપ કરો. આ સંકેત તમને હંમેશાં હોમ સ્ક્રીન પર લઈ જાય છે."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"2 આંગળી વડે સ્ક્રીનના સૌથી નીચેના ભાગથી ઉપરની તરફ સ્વાઇપ કરો. આ સંકેત તમને હંમેશાં હોમ સ્ક્રીન પર લઈ જાય છે."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"હોમ પર જાઓ"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"તમારી સ્ક્રીનની સૌથી નીચેથી ઉપરની તરફ સ્વાઇપ કરો"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"ખૂબ સરસ કામ!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"ખાતરી કરો કે તમે સ્ક્રીનની નીચેની કિનારીએથી ઉપરની તરફ સ્વાઇપ કરો છો"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"તમારી આંગળી ઊંચકતા પહેલાં તેને વિન્ડો પર થોડી વધારે વાર માટે દબાવી રાખવાનો પ્રયાસ કરો"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"ખાતરી કરો કે તમે સીધું ઉપર સ્વાઇપ કરો છો, પછી થોભી જાઓ છો"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"કોઈપણ સમયે તમારી હોમ સ્ક્રીન પર જવા માટે, તમારી સ્ક્રીનની સૌથી નીચેની બાજુએથી ઉપરની તરફ સ્વાઇપ કરો"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ખાતરી કરો કે તમે સ્ક્રીનની નીચેની કિનારીએથી ઉપરની તરફ સ્વાઇપ કરો છો."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"તમારી આંગળી ઊંચકતા પહેલાં તેને વિન્ડો પર થોડી વધારે વાર માટે દબાવી રાખવાનો પ્રયાસ કરો."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ખાતરી કરો કે તમે સીધું ઉપર સ્વાઇપ કરો છો, પછી થોભી જાઓ છો."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"સંકેતોનો ઉપયોગ કરવાની રીત વિશે તમે જાણ્યું. સંકેતો બંધ કરવા, સેટિંગમાં જાઓ."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"તમે ઍપ સ્વિચ કરવાનો સંકેત પૂર્ણ કર્યો."</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"તમે ઍપ સ્વિચ કરવાનો સંકેત પૂર્ણ કર્યો છે."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ઍપ સ્વિચ કરવા સ્વાઇપ કરો"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"એક ઍપ પરથી બીજી ઍપ પર સ્વિચ કરવા માટે, તમારી સ્ક્રીનના નીચેના ભાગથી ઉપરની તરફ સ્વાઇપ કરીને, થોડીવાર દબાવી રાખો, પછી છોડી દો."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"એક ઍપ પરથી બીજી ઍપ પર સ્વિચ કરવા માટે, 2 આંગળી વડે તમારી સ્ક્રીનના સૌથી નીચેના ભાગથી ઉપરની તરફ સ્વાઇપ કરીને, થોડીવાર દબાવી રાખો, પછી છોડી દો."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ઍપ સ્વિચ કરો"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"તમારી સ્ક્રીનના નીચેના ભાગથી ઉપરની તરફ સ્વાઇપ કરીને થોડીવાર દબાવી રાખો પછી છોડી દો"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"વાહ!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"બધું સેટ થઈ ગયું"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"થઈ ગયું"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"સેટિંગ"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ટાસ્કબાર બતાવવામાં આવ્યો"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ટાસ્કબાર છુપાવવામાં આવ્યો"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"નૅવિગેશન બાર"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"હંમેશાં ટાસ્કબાર બતાવો"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"નૅવિગેશન મોડ બદલો"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ટાસ્કબાર વિભાજક"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"સૌથી ઉપર ડાબી બાજુએ ખસેડો"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"સૌથી નીચે જમણી બાજુએ ખસેડો"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{વધુ # ઍપ બતાવો.}one{વધુ # ઍપ બતાવો.}other{વધુ # ઍપ બતાવો.}}"</string> diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml index 15ddc685f9..b108371f21 100644 --- a/quickstep/res/values-hi/strings.xml +++ b/quickstep/res/values-hi/strings.xml @@ -19,7 +19,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="recent_task_option_pin" msgid="7929860679018978258">"पिन करें"</string> + <string name="recent_task_option_pin" msgid="7929860679018978258">"पिन करना"</string> <string name="recent_task_option_freeform" msgid="48863056265284071">"फ़्रीफ़ॉर्म"</string> <string name="recents_empty_message" msgid="7040467240571714191">"हाल ही में इस्तेमाल किया गया कोई ऐप्लिकेशन नहीं है"</string> <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ऐप्लिकेशन इस्तेमाल की सेटिंग"</string> @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"सुझाए गए ऐप्लिकेशन की सुविधा चालू है"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"सुझाए गए ऐप्लिकेशन की सुविधा बंद है"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"सुझाया गया ऐप्लिकेशन: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"अपना डिवाइस घुमाएं"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"जेस्चर वाले नेविगेशन से जुड़े ट्यूटोरियल को पूरा करने के लिए अपने डिवाइस को घुमाएं"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"स्क्रीन पर बिलकुल दाएं या बाएं किनारे से स्वाइप करें"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"स्क्रीन पर दाएं या बाएं किनारे से बीच तक स्वाइप करें और फिर अपनी उंगली को स्क्रीन से हटा दें"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"देख लें कि आपने स्क्रीन की दाईं या बाईं ओर के बिलकुल किनारे से स्वाइप किया हो."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"स्क्रीन के दाएं या बाएं किनारे से स्क्रीन के बीच तक स्वाइप करें और अपनी उंगली उठा लें."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"आपने स्क्रीन के दाएं किनारे से स्वाइप करके, पिछली स्क्रीन पर वापस जाने का तरीका सीख लिया है. अब, एक ऐप से दूसरे ऐप पर जाने का तरीका सीखें."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"आपने जान लिया है कि हाथ का जेस्चर इस्तेमाल करके पिछली स्क्रीन पर वापस कैसे जाएं"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"स्क्रीन पर बिलकुल नीचे तक स्वाइप न करें"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"आपने पेज पर पीछे ले जाने वाले हाथ के जेस्चर (हाव-भाव) के बारे में जान लिया है."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"देख लें कि आप स्क्रीन पर बिलकुल नीचे तक स्वाइप न कर रहे हों."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"\'सेटिंग\' में जाकर, पीछे जाने के लिए इस्तेमाल होने वाले हाथ के जेस्चर (हाव-भाव) की संवेदनशीलता बदलें"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"पिछली स्क्रीन पर वापस जाने के लिए स्वाइप करें"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"पिछली स्क्रीन पर वापस जाने के लिए, स्क्रीन के बाएं या दाएं किनारे से बीचों-बीच तक स्वाइप करें."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"पिछली स्क्रीन पर वापस जाने के लिए, स्क्रीन के बाएं या दाएं किनारे से स्क्रीन के बीच तक दो उंगलियों से स्वाइप करें."</string> - <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"पिछली स्क्रीन पर वापस जाना"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"स्क्रीन पर बाएं या दाएं किनारे से बीच तक स्वाइप करें"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"स्क्रीन पर निचले किनारे से ऊपर की ओर स्वाइप करें"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"उंगली हटाने से पहले उसे स्क्रीन पर कहीं न रोकें"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"स्क्रीन पर सीधे ऊपर की ओर स्वाइप करें"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"आपने जान लिया कि हाथ का जेस्चर इस्तेमाल करके होम स्क्रीन पर कैसे जाएं. अब वापस जाने का तरीका जानें."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"आपने जान लिया है कि हाथ का जेस्चर इस्तेमाल करके होम स्क्रीन पर कैसे जाएं"</string> + <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"वापस जाएं"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"देख लें कि आप स्क्रीन के निचले किनारे से ऊपर की ओर स्वाइप कर रहे हों."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"देख लें कि आप स्क्रीन से अपनी उंगली उठाने से पहले, इसे कहीं न रोक रहे हों."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"देख लें कि आपने ऊपर की ओर बिलकुल सीधे स्वाइप किया हो."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"आपने होम स्क्रीन पर ले जाने वाले हाथ के जेस्चर के बारे में जान लिया है. अब, वापस जाने का तरीका जानें."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"आपने होम स्क्रीन पर ले जाने वाले हाथ के जेस्चर (हाव-भाव) के बारे में जान लिया है."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"होम स्क्रीन पर जाने के लिए स्वाइप करें"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"स्क्रीन पर नीचे से ऊपर की ओर स्वाइप करें. हाथ का यह जेस्चर आपको हमेशा होम स्क्रीन पर ले जाता है."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"स्क्रीन के सबसे नीचे से ऊपर की ओर 2 उंगलियों से स्वाइप करें. जेस्चर हमेशा होम स्क्रीन पर ले जाता है."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"होम स्क्रीन पर जाना"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"स्क्रीन पर सबसे नीचे से ऊपर की ओर स्वाइप करें"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"बहुत बढ़िया!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"स्क्रीन पर निचले किनारे से ऊपर की ओर स्वाइप करें"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"उंगली हटाने से पहले स्क्रीन को देर तक दबाकर रखें"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"स्क्रीन पर सीधे ऊपर की ओर स्वाइप करें और फिर रुकें"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"होम स्क्रीन पर जाएं"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"किसी भी समय फ़ोन की होम स्क्रीन पर जाने के लिए, फ़ोन पर सबसे नीचे से ऊपर की ओर स्वाइप करें"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"देख लें कि आप स्क्रीन के निचले किनारे से ऊपर की ओर स्वाइप कर रहे हों."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"कोशिश करें कि स्क्रीन से उंगली उठाने से पहले, इसे कुछ देर स्क्रीन पर दबाकर रखें."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"देख लें कि आप स्क्रीन पर ऊपर की तरफ़, बिलकुल सीधे स्वाइप कर रहे हों और फिर रुकें."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"आपने हाथ के जेस्चर इस्तेमाल करने सीख लिए हैं. जेस्चर बंद करने के लिए, सेटिंग में जाएं."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"आपने जान लिया है कि हाथ का जेस्चर इस्तेमाल करके ऐप्लिकेशन के बीच स्विच कैसे करें"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"आपने एक ऐप्लिकेशन से दूसरे पर जाने के लिए इस्तेमाल होने वाले हाथ के जेस्चर के बारे में जान लिया है."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"एक ऐप्लिकेशन से दूसरे पर जाने के लिए स्वाइप करें"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"एक ऐप से दूसरे पर जाने के लिए स्क्रीन पर नीचे से ऊपर की ओर स्वाइप करें, दबाकर रखें, और फिर छोड़ दें."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"एक ऐप से दूसरे पर जाने के लिए, स्क्रीन पर नीचे से ऊपर की ओर स्वाइप करें, दबाकर रखें, और फिर छोड़ दें."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"इन ऐप के बीच स्विच करने के लिए, दो उंगलियों से नीचे से ऊपर स्वाइप करें, होल्ड करें, और फिर छोड़ें."</string> - <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ऐप्लिकेशन के बीच स्विच करना"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"अपनी स्क्रीन पर सबसे नीचे से ऊपर की ओर स्वाइप करें, स्क्रीन को दबाकर रखें, और फिर छोड़ दें"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"बहुत खूब!"</string> + <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ऐप्लिकेशन के बीच स्विच करें"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"आप पूरी तरह तैयार हैं"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"हो गया"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"सेटिंग"</string> @@ -88,7 +82,7 @@ <string name="allset_title" msgid="5021126669778966707">"हो गया!"</string> <string name="allset_hint" msgid="459504134589971527">"होम पेज पर जाने के लिए, ऊपर की ओर स्वाइप करें"</string> <string name="allset_button_hint" msgid="2395219947744706291">"होम स्क्रीन पर जाने के लिए, होम बटन पर टैप करें"</string> - <string name="allset_description_generic" msgid="5385500062202019855">"अब <xliff:g id="DEVICE">%1$s</xliff:g> इस्तेमाल के लिए तैयार है"</string> + <string name="allset_description_generic" msgid="5385500062202019855">"अब <xliff:g id="DEVICE">%1$s</xliff:g> इस्तेमाल के लिए तैयार हैं"</string> <string name="default_device_name" msgid="6660656727127422487">"डिवाइस"</string> <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"सिस्टम नेविगेशन सेटिंग"</annotation></string> <string name="action_share" msgid="2648470652637092375">"शेयर करें"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"टास्कबार दिखाया गया"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"टास्कबार छिपाया गया"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"नेविगेशन बार"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"टास्कबार हमेशा दिखाएं"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"नेविगेशन का मोड बदलें"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"टास्कबार डिवाइडर"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ऊपर/बाईं तरफ़ ले जाएं"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"नीचे/दाईं तरफ़ ले जाएं"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# और ऐप्लिकेशन दिखाएं.}one{# और ऐप्लिकेशन दिखाएं.}other{# और ऐप्लिकेशन दिखाएं.}}"</string> diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml index 19786cd1f8..1aadeeac3a 100644 --- a/quickstep/res/values-hr/strings.xml +++ b/quickstep/res/values-hr/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Predlaganje apl. omogućeno"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Predlaganje apl. onemogućeno"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predviđena aplikacija: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Zakrenite uređaj"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Zakrenite uređaj da biste dovršili vodič o navigaciji pokretima"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Pazite da prijeđete prstom od krajnjeg desnog ili krajnjeg lijevog ruba"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Pazite da prijeđete prstom od desnog ili lijevog ruba do sredine zaslona i podignite prst"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Pazite da prijeđete prstom od krajnjeg desnog ili krajnjeg lijevog ruba."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Pazite da prijeđete prstom od desnog ili lijevog ruba do sredine zaslona i podignite prst."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Naučili ste kako prijeći prstom zdesna da biste se vratili. Sad saznajte kako promijeniti aplikaciju."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Izvršili ste pokret za povratak"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Pazite da ne prijeđete prstom preblizu dnu zaslona"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Izvršili ste pokret za povratak."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Pazite da ne prijeđete prstom preblizu dnu zaslona."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Osjetljivost pokreta povratka promijenite u postavkama"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Prijeđite prstom da biste se vratili"</string> - <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Da biste se vratili na prethodni zaslon, prijeđite prstom od lijevog ili desnog ruba do sredine zaslona."</string> + <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Za povratak na zadnji zaslon prijeđite prstom od lijevog ili desnog ruba do sredine zaslona."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Da biste se vratili na posljednji zaslon, prijeđite s dva prsta od lijevog ili desnog ruba do sredine zaslona."</string> - <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Povratak"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Prijeđite prstom od lijevog ili desnog ruba do sredine zaslona"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Pazite da prijeđete prstom prema gore od donjeg ruba zaslona"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Pazite da ne zastanete prije podizanja prsta"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Pazite da prijeđete prstom ravno prema gore"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Izvršili ste pokret za otvaranje početnog zaslona. Sad saznajte kako se vratiti."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Izvršili ste pokret za otvaranje početnog zaslona"</string> + <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Natrag"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Pazite da prijeđete prstom prema gore od donjeg ruba zaslona."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pazite da ne zastanete prije podizanja prsta."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Pazite da prijeđete prstom ravno prema gore."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Izvršili ste pokret za otvaranje početnog zaslona. Sad saznajte kako se vratiti."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Izvršili ste pokret za otvaranje početnog zaslona."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Prijeđite prstom da biste otvorili početni zaslon"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Prijeđite prstom od dna zaslona prema gore. Tim pokretom uvijek će se otvoriti početni zaslon."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Prijeđite s dva prsta od dna zaslona prema gore. Tim pokretom uvijek će se otvoriti početni zaslon."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Otvaranje početnog zaslona"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Prijeđite prstom od dna zaslona prema gore"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Sjajno!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Pazite da prijeđete prstom prema gore od donjeg ruba zaslona"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Pokušajte zadržati prozor dulje prije podizanja prsta"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Pazite da prijeđete prstom ravno prema gore, a zatim zastanete"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Da biste otvorili početni zaslon, prijeđite prstom od dna zaslona prema gore"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Pazite da prijeđete prstom prema gore od donjeg ruba zaslona."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Pokušajte zadržati prozor dulje prije podizanja prsta."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Pazite da prijeđete prstom ravno prema gore, a zatim zastanete."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Naučili ste koristiti pokrete. Pokrete možete isključiti u postavkama."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Izvršili ste pokret za promjenu aplikacije"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Izvršili ste pokret za promjenu aplikacije."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Povlačenje prstom za promjenu aplikacije"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Da biste promijenili aplikaciju, prijeđite prstom od dna zaslona prema gore, zadržite pritisak pa pustite."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Za promjenu aplikacije prijeđite prstom od dna zaslona prema gore, zadržite pritisak pa pustite."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Za promjenu aplikacije prijeđite s dva prsta od dna zaslona prema gore, zadržite pritisak i pustite."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Promjena aplikacije"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Prijeđite prstom od dna zaslona prema gore, zadržite pritisak pa pustite"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Odlično!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Sve je spremno"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Gotovo"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Postavke"</string> @@ -103,11 +97,11 @@ <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Preskoči"</string> <string name="accessibility_rotate_button" msgid="4771825231336502943">"Zakretanje zaslona"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Upute za traku sa zadacima"</string> - <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Povucite aplikaciju u stranu radi istodobne upotrebe dviju aplikacija"</string> + <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Povucite apl. u stranu radi istodobne upotrebe 2 aplikacije"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Polako prijeđite prstom prema gore za prikaz trake sa zadacima"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Primajte prijedloge aplikacija na temelju svoje rutine"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Uključite navigaciju pokretima u postavkama da bi se traka sa zadacima automatski sakrila"</string> - <string name="taskbar_edu_features" msgid="3320337287472848162">"Učinite više pomoću trake sa zadacima"</string> + <string name="taskbar_edu_features" msgid="3320337287472848162">"Učinite više uz pomoć trake sa zadacima"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Zatvori"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Gotovo"</string> <string name="taskbar_button_home" msgid="2151398979630664652">"Početna"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Traka sa zadacima prikazana"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Traka sa zadacima skrivena"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigacijska traka"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Uvijek prikaži traku zadataka"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Promijeni način navigacije"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Razdjelnik trake sa zadacima"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premjesti gore/lijevo"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premjesti dolje/desno"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Prikaži više aplikacija (još #).}one{Prikaži više aplikacija (još #).}few{Prikaži više aplikacija (još #).}other{Prikaži više aplikacija (još #).}}"</string> diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml index 6e4f860237..ba75d4f00a 100644 --- a/quickstep/res/values-hu/strings.xml +++ b/quickstep/res/values-hu/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Alkalmazásjavaslatok engedélyezve"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Alkalmazásjavaslatok letiltva"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Várható alkalmazás: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Forgassa el eszközét"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Forgassa el eszközét a kézmozdulatokkal való navigáció útmutatójának befejezéséhez"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Csúsztasson a képernyő jobb vagy bal széléről."</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Csúsztassa ujját a képernyő jobb vagy bal széléről a képernyő közepéig, majd emelje fel."</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Csúsztasson a képernyő jobb vagy bal széléről."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Csúsztassa ujját a képernyő jobb vagy bal széléről a képernyő közepéig, majd emelje fel."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Megtanulta, hogyan léphet vissza jobbról csúsztatva. A következő az appok közötti váltás."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Teljesítette a visszalépési kézmozdulatot."</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Ne csúsztasson túl közel a képernyő aljához."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Teljesítette a visszalépési kézmozdulatot."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Ne csúsztasson túl közel a képernyő aljához."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"A vissza mozdulat érzékenysége a Beállításokban módosítható"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Csúsztasson a visszalépéshez"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Ha visszatérne a legutóbbi képernyőre, csúsztasson a képernyő közepére a bal vagy a jobb széléről."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Ha vissza szeretne térni a legutóbbi képernyőre, csúsztasson gyorsan két ujjal a képernyő bal vagy jobb széléről a közepe felé."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Vissza"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Csúsztasson bal vagy jobb szélről a képernyő közepe felé."</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Csúsztasson felfelé a képernyő aljától."</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Ne álljon meg, mielőtt elengedi a képernyőt."</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Csúsztasson egyenesen felfelé."</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Teljesítette a kezdőképernyőre lépés kézmozdulatát. Most megtanulhatja, hogyan léphet vissza."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Teljesítette a kezdőképernyőre lépés kézmozdulatát."</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Csúsztasson felfelé a képernyő aljától."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Ne álljon meg, mielőtt elengedi a képernyőt."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Csúsztasson egyenesen felfelé."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Teljesítette a kezdőképernyőre lépés kézmozdulatát. Most megtanulhatja, hogyan léphet vissza."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Teljesítette a kezdőképernyőre lépés kézmozdulatát."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Csúsztatás a kezdőképernyőre lépéshez"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Csúsztassa ujját felfelé a képernyő aljától. Ez a mozdulat mindig a kezdőképernyőre visz."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Csúsztasson felfelé két ujjal a képernyő aljáról. Ez a kézmozdulat mindig a kezdőképernyőre viszi."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Ugrás a kezdőképernyőre"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Húzza ujját felfelé a képernyő aljától."</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Kiváló!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Csúsztasson felfelé a képernyő aljától."</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Próbálja tovább lenyomva tartani az ablakot, mielőtt elengedi a képernyőt."</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Csúsztasson egyenesen felfelé, majd várjon egy kicsit"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Ha vissza szeretne térni a kezdőképernyőre, bármikor felfelé csúsztathat ujjával a képernyő aljáról"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Csúsztasson felfelé a képernyő aljától."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Próbálja tovább lenyomva tartani az ablakot, mielőtt elengedi a képernyőt."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Csúsztasson egyenesen felfelé, majd várjon egy kicsit."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Eddig megismerhette a kézmozdulatok használatát. A kézmozdulatokat a Beállításokban kapcsolhatja ki."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Teljesítette az alkalmazásváltás kézmozdulatát."</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Teljesítette az alkalmazásváltás kézmozdulatát."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Alkalmazásváltás csúsztatással"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Appok közti váltáshoz csúsztasson felfelé a kép aljáról, tartsa lenyomva az ujját, majd emelje fel."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Appváltáshoz csúsztasson fel két ujjal a kép aljáról, tartsa lenyomva ujjait, majd emelje fel őket."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Váltás az alkalmazások között"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Csúsztasson felfelé a képernyő aljáról, tartsa lenyomva ujját, majd emelje fel"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Szép munka!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Minden kész"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Kész"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Beállítások"</string> @@ -88,7 +82,7 @@ <string name="allset_title" msgid="5021126669778966707">"Kész is!"</string> <string name="allset_hint" msgid="459504134589971527">"Felfelé csúsztatva megjelenik a kezdőképernyő"</string> <string name="allset_button_hint" msgid="2395219947744706291">"A kezdőképernyőre való lépéshez koppintson a kezdőképernyő gombra"</string> - <string name="allset_description_generic" msgid="5385500062202019855">"Készen áll az <xliff:g id="DEVICE">%1$s</xliff:g> használatára"</string> + <string name="allset_description_generic" msgid="5385500062202019855">"Készen áll a(z) <xliff:g id="DEVICE">%1$s</xliff:g> használatára"</string> <string name="default_device_name" msgid="6660656727127422487">"eszköz"</string> <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Rendszer-navigációs beállítások"</annotation></string> <string name="action_share" msgid="2648470652637092375">"Megosztás"</string> @@ -104,7 +98,7 @@ <string name="accessibility_rotate_button" msgid="4771825231336502943">"Képernyő elforgatása"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Tálca használatának ismertetése"</string> <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Húzzon egy appot oldalra, ha kettőt használna egyidejűleg"</string> - <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Lassan csúsztassa fel az ujját a Feladatsáv megjelenítéséhez"</string> + <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Csúsztassa ujját lassan fel a Feladatsáv megjelenítéséhez"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Alkalmazásjavaslatokat kaphat a rutinja alapján"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"A Feladatsáv automatikus elrejtéséhez aktiválja a navigációs kézmozdulatokat a beállításokban"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Jobban kihasználhatja a Feladatsávot"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Feladatsáv megjelenítve"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Feladatsáv elrejtve"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigációs sáv"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Mindig megjelenő feladatsáv"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Navigációs mód módosítása"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Feladatsáv-elválasztó"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mozgatás felülre vagy a bal oldalra"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mozgatás alulra vagy a jobb oldalra"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# további alkalmazás megjelenítése.}other{# további alkalmazás megjelenítése.}}"</string> diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml index b6bfa4470e..43a7c5d4a1 100644 --- a/quickstep/res/values-hy/strings.xml +++ b/quickstep/res/values-hy/strings.xml @@ -44,43 +44,37 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"«Առաջարկվող հավելվածներ» գործառույթը միացված է"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"«Առաջարկվող հավելվածներ» գործառույթն անջատված է"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Առաջարկվող հավելված՝ <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Պտտեք սարքը"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Պտտեք սարքը՝ ժեստերով նավիգացիայի ուղեցույցն ավարտելու համար"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Համոզվեք, որ մատը սահեցնում եք էկրանի աջ կամ ձախ եզրից"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Մատը սահեցրեք էկրանի աջ կամ ձախ եզրից դեպի կենտրոն և բաց թողեք"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Համոզվեք, որ մատը սահեցնում եք էկրանի աջ կամ ձախ եզրից։"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Մատը սահեցրեք էկրանի աջ կամ ձախ եզրից դեպի կենտրոն և բաց թողեք։"</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Դուք սովորեցիք՝ ինչպես մատը աջից սահեցնելով հետ գնալ։ Այժմ սովորենք՝ ինչպես անցնել մի հավելվածից մյուսը։"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Դուք սովորեցիք հետ գնալու ժեստը"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Համոզվեք, որ մատը չափազանց մոտ չեք սահեցնում էկրանի ներքևի հատվածին"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Դուք սովորեցիք հետ գնալու ժեստը։"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Համոզվեք, որ մատը չափազանց մոտ չեք սահեցնում էկրանին ներքևի հատվածին։"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Հետ գնալու ժեստի զգայունությունը փոփոխեք կարգավորումներում"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Սահեցրեք մատը՝ հետ գնալու համար"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Վերջին էկրանին վերադառնալու համար էկրանի աջ կամ ձախ եզրից մատը սահեցրեք դեպի կենտրոն։"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Վերջին էկրանին վերադառնալու համար 2 մատը սահեցրեք ձախ կամ աջ եզրից դեպի կենտրոն։"</string> - <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Հետ գնալ"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Էկրանի աջ կամ ձախ եզրից մատը սահեցրեք դեպի կենտրոն"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Համոզվեք, որ մատն էկրանի ներքևի եզրից վերև եք սահեցնում"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Համոզվեք, որ դադար չեք տալիս նախքան բաց թողնելը"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Համոզվեք, որ մատն ուղիղ վերև եք սահեցնում"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Դուք սովորեցիք հիմնական էկրան անցնելու ժեստը։ Այժմ սովորենք՝ ինչպես հետ գնալ։"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Դուք սովորեցիք հիմնական էկրան անցնելու ժեստը"</string> + <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Վերադարձ հետ"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Համոզվեք, որ մատն էկրանի ներքևի եզրից վերև եք սահեցնում։"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Համոզվեք, որ դադար չեք տալիս նախքան բաց թողնելը։"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Համոզվեք, որ մատն ուղիղ վերև եք սահեցնում։"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Դուք սովորեցիք հիմնական էկրան անցնելու ժեստը։ Այժմ սովորենք՝ ինչպես հետ գնալ։"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Դուք սովորեցիք հիմնական էկրան անցնելու ժեստը։"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Սահեցրեք մատը՝ հիմնական էկրան անցնելու համար"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Մատը սահեցրեք էկրանի ներքևից վերև։ Այս ժեստը բացում է հիմնական էկրանը։"</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Երկու մատը էկրանի ներքևից սահեցրեք վերև։ Այս ժեստը բացում է հիմնական էկրանը։"</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Անցնել հիմնական էկրան"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Մատը սահեցրեք էկրանի ներքևից վերև"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Կեցցե՛ք"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Համոզվեք, որ մատն էկրանի ներքևի եզրից վերև եք սահեցնում"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Նախքան բաց թողնելը փորձեք հնարավորինս երկար պահել պատուհանը"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Համոզվեք, որ մատն ուղիղ վերև եք սահեցնում, այնուհետև դադար տվեք"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Հիմնական էկրան վերադառնալու համար մատը էկրանի ներքևից սահեցրեք վերև"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Համոզվեք, որ մատն էկրանի ներքևի եզրից վերև եք սահեցնում։"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Նախքան բաց թողնելը փորձեք հնարավորինս երկար պահել պատուհանը։"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Համոզվեք, որ մատն ուղիղ վերև եք սահեցնում, այնուհետև դադար տվեք։"</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Դուք սովորեցիք՝ ինչպես օգտագործել ժեստերը։ Ժեստերը կարող եք անջատել կարգավորումներում։"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Դուք սովորեցիք մի հավելվածից մյուսն անցնելու ժեստը"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Դուք սովորեցիք մի հավելվածից մյուսն անցնելու ժեստը։"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Մատը սահեցրեք՝ մյուս հավելվածին անցնելու համար"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Մեկ հավելվածից մյուսն անցնելու համար մատը էկրանի ներքևից սահեցրեք վերև, պահեք, ապա բաց թողեք։"</string> - <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Մեկ հավելվածից մյուսն անցնելու համար 2 մատը էկրանի ներքևից սահեցրեք վերև, պահեք, ապա բաց թողեք։"</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Մեկ հավելվածից մյուսն անցնելու համար մատը էկրանի ներքևից սահեցրեք վերև, ապա հեռացրեք այն էկրանից։"</string> + <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Մեկ հավելվածից մյուսն անցնելու համար 2 մատը էկրանի ներքևից սահեցրեք վերև, ապա հեռացրեք այն էկրանից։"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Անցում մեկ հավելվածից մյուսին"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Մատը սահեցրեք էկրանի ներքևից վերև, պահեք և բաց թողեք"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Հիանալի՛ է"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Պատրաստ է"</string> - <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Ավարտել"</string> + <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Պատրաստ է"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Կարգավորումներ"</string> <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Նորից փորձեք"</string> <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Գերազանց է"</string> @@ -88,7 +82,7 @@ <string name="allset_title" msgid="5021126669778966707">"Պատրաստ է"</string> <string name="allset_hint" msgid="459504134589971527">"Մատը սահեցրեք վերև՝ հիմնական էկրան անցնելու համար"</string> <string name="allset_button_hint" msgid="2395219947744706291">"Հիմնական էկրան վերադառնալու համար սեղմեք գլխավոր էկրանի կոճակը"</string> - <string name="allset_description_generic" msgid="5385500062202019855">"Դուք արդեն կարող եք օգտագործել ձեր <xliff:g id="DEVICE">%1$s</xliff:g>ը"</string> + <string name="allset_description_generic" msgid="5385500062202019855">"Դուք արդեն կարող եք օգտագործել ձեր <xliff:g id="DEVICE">%1$s</xliff:g> սարքը"</string> <string name="default_device_name" msgid="6660656727127422487">"սարք"</string> <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Նավիգացիայի համակարգային կարգավորումներ"</annotation></string> <string name="action_share" msgid="2648470652637092375">"Կիսվել"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Խնդրագոտին ցուցադրվում է"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Խնդրագոտին թաքցված է"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Նավիգացիայի գոտի"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Միշտ ցուցադրել հավելվածները"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Փոխել նավիգացիայի ռեժիմը"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Հավելվածների վահանակի բաժանիչ"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Տեղափոխել վերևի ձախ անկյուն"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Տեղափոխել ներքևի աջ անկյուն"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Ցուցադրել ևս # հավելված։}one{Ցուցադրել ևս # հավելված։}other{Ցուցադրել ևս # հավելված։}}"</string> diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml index 27833a8a86..6f9d7c6840 100644 --- a/quickstep/res/values-in/strings.xml +++ b/quickstep/res/values-in/strings.xml @@ -44,49 +44,43 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Saran aplikasi diaktifkan"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Saran aplikasi dinonaktifkan"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplikasi yang diprediksi: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Putar perangkat Anda"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Putar perangkat Anda untuk menyelesaikan tutorial navigasi gestur"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Pastikan Anda menggeser dari tepi ujung kanan atau ujung kiri"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Pastikan Anda menggeser dari tepi kanan atau kiri ke tengah layar, lalu lepaskan"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Pastikan Anda menggeser dari tepi ujung kanan atau ujung kiri."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Pastikan Anda menggeser dari tepi kanan atau kiri ke tengah layar, lalu lepaskan."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Anda telah belajar cara geser dari kanan untuk kembali. Berikutnya, pelajari cara beralih aplikasi."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Anda telah menyelesaikan gestur kembali"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Pastikan Anda tidak menggeser terlalu dekat ke bagian bawah layar"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Anda telah menyelesaikan gestur kembali."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Pastikan Anda tidak menggeser terlalu dekat ke bagian bawah layar."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Untuk mengubah sensitivitas gestur kembali, buka Setelan"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Geser untuk kembali"</string> - <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Untuk kembali ke layar yang sebelumnya dibuka, geser dari tepi kiri atau kanan ke tengah layar."</string> + <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Untuk kembali ke layar terakhir, geser dari tepi kiri atau kanan ke tengah layar."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Untuk kembali ke layar terakhir, geser dengan 2 jari dari tepi kiri atau kanan ke tengah layar."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Kembali"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Geser dari tepi kiri atau kanan ke tengah layar"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Pastikan Anda menggeser ke atas dari tepi bawah layar"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Pastikan Anda tidak berhenti sebelum melepaskan sentuhan"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Pastikan Anda menggeser lurus ke atas"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Anda telah menyelesaikan gestur buka layar utama. Berikutnya, pelajari cara melakukan gestur kembali."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Anda telah menyelesaikan gestur buka layar utama"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Pastikan Anda menggeser ke atas dari tepi bawah layar."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pastikan Anda tidak menjeda sebelum melepaskan."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Pastikan Anda menggeser lurus ke atas."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Anda telah menyelesaikan gestur menuju Layar utama. Selanjutnya, pelajari cara beralih kembali."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Anda telah menyelesaikan gestur menuju Layar utama."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Geser untuk beralih ke layar utama"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Geser ke atas dari bagian bawah layar. Gestur ini akan selalu membawa Anda ke Layar utama."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Geser ke atas dengan 2 jari dari bawah layar. Gestur ini akan selalu membawa Anda ke Layar utama."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Buka layar utama"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Geser ke atas dari bagian bawah layar"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Bagus."</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Pastikan Anda menggeser ke atas dari tepi bawah layar"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Coba tahan jendela lebih lama sebelum melepaskan"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Pastikan Anda menggeser lurus ke atas, lalu berhenti sejenak"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Untuk membuka layar utama kapan saja, geser ke atas dari bagian bawah layar"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Pastikan Anda menggeser ke atas dari tepi bawah layar."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Coba tahan jendela lebih lama sebelum melepaskan."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Pastikan Anda menggeser lurus ke atas, lalu menjedanya."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Anda telah mempelajari cara menggunakan gestur. Untuk menonaktifkan gestur, buka Setelan."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Anda telah menyelesaikan gestur beralih aplikasi"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Anda telah menyelesaikan gestur beralih aplikasi."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Geser untuk beralih aplikasi"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Untuk beralih dari satu aplikasi ke aplikasi lain, geser ke atas dari bagian bawah layar, tahan, lalu lepaskan."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Untuk beralih antar-aplikasi, geser ke atas dari bagian bawah layar, tahan, lalu lepaskan."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Untuk beralih antar-aplikasi, geser ke atas dengan 2 jari dari bawah layar, tahan, lalu lepaskan."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Beralih aplikasi"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Geser ke atas dari bagian bawah layar, tahan, kemudian lepaskan"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Bagus."</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Semua siap"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Selesai"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Setelan"</string> <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Coba lagi"</string> <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bagus!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> - <string name="allset_title" msgid="5021126669778966707">"Selesai!"</string> - <string name="allset_hint" msgid="459504134589971527">"Geser ke atas untuk membuka Layar utama"</string> + <string name="allset_title" msgid="5021126669778966707">"Semua siap."</string> + <string name="allset_hint" msgid="459504134589971527">"Geser ke atas untuk beralih ke layar utama"</string> <string name="allset_button_hint" msgid="2395219947744706291">"Ketuk tombol layar utama untuk membuka layar utama"</string> <string name="allset_description_generic" msgid="5385500062202019855">"Anda sudah siap untuk mulai menggunakan <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="default_device_name" msgid="6660656727127422487">"perangkat"</string> @@ -94,7 +88,7 @@ <string name="action_share" msgid="2648470652637092375">"Bagikan"</string> <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string> <string name="action_split" msgid="2098009717623550676">"Pisahkan"</string> - <string name="toast_split_select_app" msgid="8464310533320556058">"Ketuk aplikasi lain untuk memakai layar terpisah"</string> + <string name="toast_split_select_app" msgid="8464310533320556058">"Ketuk apl lain untuk menggunakan layar terpisah"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Pilih aplikasi lain untuk memakai layar terpisah"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Tindakan ini tidak diizinkan oleh aplikasi atau organisasi Anda"</string> <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Lewati tutorial gestur?"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar ditampilkan"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar disembunyikan"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Menu navigasi"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Selalu tampilkan Taskbar"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Ubah mode navigasi"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Pemisah Taskbar"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Pindahkan ke atas/kiri"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pindahkan ke bawah/kanan"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Tampilkan # aplikasi lain.}other{Tampilkan # aplikasi lain.}}"</string> diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml index 3ff7185b99..f58e6de718 100644 --- a/quickstep/res/values-is/strings.xml +++ b/quickstep/res/values-is/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Kveikt á tillögum að forritum"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Slökkt er á tillögðum forritum"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Tillaga að forriti: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Snúðu tækinu"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Snúðu tækinu til að ljúka leiðsögn um bendingastjórnun"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Passaðu að strjúka frá jaðri hægri eða vinstri brúnar"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Passaðu að strjúka frá jaðri hægri eða vinstri brúnar að miðju skjásins og sleppa síðan"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Passaðu að strjúka frá jaðri hægri eða vinstri brúnar."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Passaðu að strjúka frá jaðri hægri eða vinstri brúnar að miðju skjásins og sleppa síðan."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Þú lærðir að strjúka frá hægri til að bakka. Næst skaltu læra hvernig þú skiptir á milli forrita."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Þú laukst við að kynna þér bendinguna „til baka“"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Passaðu að strjúka ekki of nálægt neðri brún skjásins"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Þú laukst við að kynna þér bendinguna „til baka“."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Passaðu að strjúka ekki of nálægt neðri brún skjásins."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Til að breyta næmi til baka-bendingar ferðu í stillingar"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Strjúktu til að fara til baka"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Til að fara til baka á síðasta skjá skaltu strjúka frá vinstri eða hægri brún að miðju skjásins."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Strjúktu frá vinstri eða hægri brún að miðju skjásins með 2 fingrum til að fara aftur á síðasta skjá."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Til baka"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Strjúktu frá vinstri eða hægri brún að miðju skjásins"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Passaðu að strjúka upp frá neðri brún skjásins"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Passaðu að stoppa ekki áður en þú sleppir"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Passaðu að strjúka beint upp"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Þú laukst við að kynna þér bendinguna „heim“. Næst skaltu læra hvernig þú ferð „til baka“."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Þú laukst við að kynna þér bendinguna „heim“"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Passaðu að strjúka upp frá neðri brún skjásins."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Passaðu að stoppa ekki áður en þú sleppir."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Passaðu að strjúka beint upp."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Þú laukst við að kynna þér bendinguna „heim“. Næst skaltu læra hvernig þú ferð „til baka“."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Þú laukst við að kynna þér bendinguna „heim“."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Strjúktu til að fara heim"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Strjúktu upp frá neðri hluta skjásins. Þetta flytur þig alltaf á heimaskjáinn."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Strjúktu frá neðri brún skjásins með 2 fingrum. Þessi bending opnar ávallt heimaskjáinn."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Fara á heimaskjá"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Strjúktu upp frá neðri hluta skjásins"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Vel gert!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Passaðu að strjúka upp frá neðri brún skjásins"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Prófaðu að halda fingrinum lengur á glugganum áður en þú sleppir"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Passaðu að strjúka beint upp og stoppa svo"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Strjúktu upp frá neðsta hluta skjásins til að opna heimskjáinn hvenær sem er"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Passaðu að strjúka upp frá neðri brún skjásins."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Prófaðu að halda fingrinum lengur á glugganum áður en þú sleppir."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Passaðu að strjúka beint upp og stoppa svo."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Þú kynntir þér hvernig á að nota bendingar. Opnaðu stillingar til að slökkva á bendingum."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Þú laukst við að kynna þér bendinguna „skipta um forrit“"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Þú laukst við að kynna þér bendinguna „skipta um forrit“."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Strjúktu til að skipta á milli forrita"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Strjúktu upp frá neðri hluta skjásins, haltu og slepptu svo til að skipta á milli forrita."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Strjúktu upp frá neðri brún skjásins með 2 fingrum, haltu og slepptu til að skipta á milli forrita."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Skipta um forrit"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Strjúktu upp frá neðri hluta skjásins, haltu inni og slepptu svo"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Vel gert!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Allt til reiðu"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Lokið"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Stillingar"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Forritastika sýnd"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Forritastika falin"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Yfirlitsstika"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Alltaf sýna forritastiku"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Breyta leiðsagnarstillingu"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Skipting forritastiku"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Færa efst/til vinstri"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Færa neðst/til hægri"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Sýna # forrit í viðbót.}one{Sýna # forrit í viðbót.}other{Sýna # forrit í viðbót.}}"</string> diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml index 5777911cb7..17e379fe2f 100644 --- a/quickstep/res/values-it/strings.xml +++ b/quickstep/res/values-it/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"La funzionalità app suggerite è attiva"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"La funzionalità app suggerite è disattivata"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App prevista: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Ruota il dispositivo"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Ruota il dispositivo per completare il tutorial relativo alla navigazione tramite gesti"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Assicurati di scorrere dal bordo all\'estrema destra o all\'estrema sinistra"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Assicurati di scorrere dal bordo destro o sinistro verso il centro dello schermo e solleva il dito"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Assicurati di scorrere dal bordo all\'estrema destra o all\'estrema sinistra."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Assicurati di scorrere dal bordo destro o sinistro verso il centro dello schermo e solleva il dito."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Hai imparato a scorrere da destra per tornare indietro. Ora impara come passare da un\'app all\'altra."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Hai completato il gesto Indietro"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Assicurati di non scorrere troppo vicino alla parte inferiore dello schermo"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Hai completato il gesto Indietro."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Assicurati di non scorrere troppo vicino alla parte inferiore dello schermo."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Usa Impostazioni per cambiare sensibilità del gesto Indietro"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Scorri per tornare indietro"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Per tornare all\'ultima schermata, scorri dal bordo sinistro o destro verso il centro dello schermo."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Per tornare all\'ultima schermata, scorri con 2 dita dal bordo sinistro o destro verso il centro dello schermo."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Vai indietro"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Scorri dal bordo sinistro o destro verso il centro dello schermo"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Assicurati di scorrere verso l\'alto dal bordo inferiore dello schermo"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Assicurati di non fare pause prima di sollevare il dito"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Assicurati di scorrere verso l\'alto senza fermarti"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Hai completato il gesto Vai alla schermata Home. Ora, impara come tornare indietro."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Hai completato il gesto Vai alla schermata Home"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Assicurati di scorrere verso l\'alto dal bordo inferiore dello schermo."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Assicurati di non fare pause prima di sollevare il dito."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Assicurati di scorrere verso l\'alto senza fermarti."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Hai completato il gesto per andare alla schermata Home. Ora, impara come tornare indietro."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Hai completato il gesto Vai alla schermata Home."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Scorri per andare alla schermata Home"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Scorri verso l\'alto dalla parte inferiore dello schermo. Questo gesto ti porta sempre alla schermata Home."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Scorri verso l\'alto con 2 dita dal basso. Questo gesto ti porta sempre alla schermata Home."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Vai alla schermata Home"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Scorri verso l\'alto dalla parte inferiore dello schermo"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Ottimo lavoro!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Assicurati di scorrere verso l\'alto dal bordo inferiore dello schermo"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Prova a tenere premuta la finestra più a lungo prima di rilasciarla"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Assicurati di scorrere verso l\'alto senza fermarti, poi fai una pausa"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Per andare alla schermata Home in qualsiasi momento, scorri sullo schermo dal basso verso l\'alto"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Assicurati di scorrere verso l\'alto dal bordo inferiore dello schermo."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Prova a tenere premuta la finestra più a lungo prima di rilasciarla."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Assicurati di scorrere verso l\'alto senza fermarti, poi fai una pausa."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Hai imparato a usare i gesti. Per disattivarli, vai alle Impostazioni."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Hai completato il gesto Cambia app"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Hai completato il gesto Cambia app."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Scorri per passare da un\'app all\'altra"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Per spostarti tra le app, scorri dal basso verso l\'alto sullo schermo, tieni premuto e rilascia."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Per spostarti tra le app, scorri verso l\'alto dal fondo dello schermo, tieni premuto e rilascia."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Per spostarti tra le app, scorri verso l\'alto con 2 dita, tieni premuto e rilascia."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Cambia app"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Scorri verso l\'alto dalla parte inferiore dello schermo, tieni premuto e poi rilascia"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Ben fatto!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Fatto"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Fine"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Impostazioni"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barra delle app visualizzata"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barra delle app nascosta"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra di navigazione"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Mostra sempre barra delle app"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Cambia modalità di navigazione"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divisore barra delle app"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sposta in alto/a sinistra"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sposta in basso/a destra"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Mostra # altra app.}other{Mostra altre # app.}}"</string> diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml index 5c960c9bd2..dc30dc686a 100644 --- a/quickstep/res/values-iw/strings.xml +++ b/quickstep/res/values-iw/strings.xml @@ -44,46 +44,40 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"התכונה \'הצעות לאפליקציות\' מופעלת"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ההצעות לאפליקציות מושבתות"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"האפליקציות החזויות: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"צריך לסובב את המכשיר"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"צריך לסובב את המכשיר כדי להשלים את המדריך לניווט באמצעות תנועות"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"חשוב להחליק מהקצה השמאלי או הימני"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"חשוב להחליק מהקצה השמאלי או הימני למרכז המסך ואז לשחרר"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"חשוב להקפיד להחליק מהקצה השמאלי או הימני."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"חשוב להקפיד להחליק מהקצה השמאלי או הימני למרכז המסך ואז לשחרר."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"למדת איך להחליק מצד ימין כדי לחזור אחורה. בשלב הבא לומדים איך לעבור בין אפליקציות."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"השלמת את התנועה \'חזרה אחורה\'"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"חשוב שלא להחליק קרוב מדי לתחתית המסך"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"השלמת את תנועת \'הקודם\'."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"חשוב להקפיד שלא להחליק קרוב מדי לתחתית המסך."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"כדי לשנות את מידת הרגישות של תנועת החזרה, יש לעבור להגדרות"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"יש להחליק כדי לחזור"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"כדי לחזור למסך הקודם, יש להחליק מהקצה השמאלי או הימני למרכז המסך."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"כדי לחזור למסך הקודם, יש להחליק עם שתי אצבעות מהקצה השמאלי או הימני למרכז המסך."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"הקודם"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"מחליקים מהקצה השמאלי או הימני למרכז המסך"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"חשוב להחליק למעלה מהקצה התחתון של המסך"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"חשוב שלא לחכות לפני שחרור האצבע"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"חשוב להחליק ישר למעלה"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"השלמת את תנועת המעבר למסך הבית. בשלב הבא נראה איך לחזור למסך הקודם."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"השלמת את תנועת המעבר למסך הבית"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"חשוב להקפיד להחליק למעלה מהקצה התחתון של המסך."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"חשוב לוודא שלא מחכים לפני שמשחררים."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"חשוב להקפיד להחליק ישר למעלה."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"השלמת את תנועת המעבר למסך הבית. בשלב הבא לומדים איך לחזור למסך הקודם."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"השלמת את תנועת המעבר למסך הבית."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"יש להחליק כדי לעבור למסך הבית"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"החלקה למעלה מתחתית המסך תמיד תעביר אותך למסך הבית."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"יש להחליק למעלה עם שתי אצבעות מתחתית המסך. התנועה הזו תמיד מעבירה אותך למסך הבית."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"מעבר למסך הבית"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"מחליקים כלפי מעלה מהחלק התחתון של המסך"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"מעולה!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"חשוב להחליק למעלה מהקצה התחתון של המסך"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"כדאי לנסות להחזיק את החלון זמן רב יותר לפני שחרור האצבע"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"חשוב להחליק ישר למעלה ואז להמתין"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"למעבר למסך הבית בכל שלב, צריך להחליק למעלה מהחלק התחתון של המסך"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"חשוב להקפיד להחליק למעלה מהקצה התחתון של המסך."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"אפשר להחזיק את החלון זמן רב יותר לפני שמשחררים."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"חשוב להקפיד להחליק ישר למעלה ואז להמתין."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"למדת איך להשתמש בתנועות. ניתן להשבית את התנועות ב\'הגדרות\'."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"השלמת את תנועת המעבר בין האפליקציות"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"השלמת את תנועת המעבר בין האפליקציות."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"יש להחליק כדי לעבור בין אפליקציות"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"כדי לעבור בין אפלקציות, יש להחליק למעלה מתחתית המסך, להחזיק ולאחר מכן לשחרר."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"כדי לעבור בין אפלקציות, יש להחליק למעלה עם שתי אצבעות מתחתית המסך, להחזיק ולאחר מכן לשחרר."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"מעבר בין אפליקציות"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"מחליקים כלפי מעלה מתחתית המסך, מחזיקים ואז משחררים"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"כל הכבוד!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"הכול מוכן"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"סיום"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"הגדרות"</string> <string name="gesture_tutorial_try_again" msgid="65962545858556697">"ניסיון חוזר"</string> - <string name="gesture_tutorial_nice" msgid="2936275692616928280">"יפה!"</string> + <string name="gesture_tutorial_nice" msgid="2936275692616928280">"איזה יופי!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"מדריך <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"הכול מוכן!"</string> <string name="allset_hint" msgid="459504134589971527">"כדי לחזור לדף הבית, מחליקים כלפי מעלה"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"סרגל האפליקציות מוצג"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"סרגל האפליקציות מוסתר"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"סרגל הניווט"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"סרגל האפליקציות מוצג תמיד"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"שינוי מצב הניווט"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"המחיצה בסרגל האפליקציות"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"העברה לפינה השמאלית/העליונה"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"העברה לפינה הימנית/התחתונה"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{הצגת אפליקציה אחת (#) נוספת.}one{הצגת # אפליקציות נוספות.}two{הצגת # אפליקציות נוספות.}other{הצגת # אפליקציות נוספות.}}"</string> diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml index 13a3425238..75e3bf8a1d 100644 --- a/quickstep/res/values-ja/strings.xml +++ b/quickstep/res/values-ja/strings.xml @@ -44,45 +44,39 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"アプリの候補表示が有効です"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"アプリの候補は無効です"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"予測されたアプリ: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"デバイスを回転してください"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ジェスチャー ナビゲーションのチュートリアルを終了するには、デバイスを回転してください"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"右端または左端からスワイプしてください"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"画面の右端または左端から中央に向かってスワイプし、指を離してください"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"右端または左端からスワイプしてください。"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"画面の右端または左端から中央に向かってスワイプし、指を離してください。"</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"右側からスワイプして前の画面に戻る方法を学習しました。次は、アプリを切り替える方法を覚えましょう。"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"「戻る」操作を学習しました"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"スワイプする際は画面の下部に近づきすぎないようにしましょう"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"「戻る」操作を学習しました。"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"スワイプする際は画面の下部に近づきすぎないようにしましょう。"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"「戻る」操作の感度を変更するには [設定] に移動します"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"スワイプで戻る"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"直前の画面に戻るには、画面の左端または右端から中央に向かってスワイプします。"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"直前の画面に戻るには、2 本の指で画面の左端または右端から中央に向かってスワイプします。"</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"戻る"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"画面の左端または右端から中央に向かってスワイプします"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"画面の下端から上にスワイプしてください"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"指を離す前にいったん止めないでください"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"まっすぐ上にスワイプしてください"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"「ホームに移動」操作を学習しました。次は、前の画面に戻る方法を覚えましょう。"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"「ホームに移動」操作を学習しました"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"画面の下端から上にスワイプしてください。"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"指を離す前にいったん止めないでください。"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"まっすぐ上にスワイプしてください。"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"「ホームに戻る」操作を学習しました。次は、前の画面に戻る方法を覚えましょう。"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"「ホームに戻る」操作を学習しました。"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"スワイプでホームに戻る"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"画面を下から上にスワイプします。この操作でいつでもホーム画面に戻れます。"</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"2 本の指で画面下部から上にスワイプします。この操作で常にホーム画面に戻ります。"</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ホームに移動"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"画面を下から上にスワイプします"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"よくできました!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"画面の下端から上にスワイプしてください"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"ウィンドウをもう少し長く押してから指を離すようにしてみましょう"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"まっすぐ上にスワイプしてから、いったん指を止めてください"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"画面を下から上にスワイプすると、ホーム画面にいつでも移動できます"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"画面の下端から上にスワイプしてください。"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"ウィンドウをもう少し長く押してから指を離すようにしてみましょう。"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"まっすぐ上にスワイプしてから、いったん指を止めてください。"</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"主なジェスチャーについて学びました。ジェスチャーを OFF にするには、設定に移動してください。"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"「アプリの切り替え」操作を学習しました"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"「アプリを切り替える」操作を完了しました。"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"スワイプでアプリを切り替える"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"アプリを切り替えるには、画面を下から上にスワイプして長押しし、指を離します。"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"アプリを切り替えるには、2 本の指で画面下部から上にスワイプしたまま長押しし、指を離します。"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"アプリの切り替え"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"画面を下から上にスワイプして長押しし、指を離します"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"完了です!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"設定完了"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"完了"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"設定"</string> - <string name="gesture_tutorial_try_again" msgid="65962545858556697">"もう一度行ってください"</string> + <string name="gesture_tutorial_try_again" msgid="65962545858556697">"もう一度"</string> <string name="gesture_tutorial_nice" msgid="2936275692616928280">"その調子です!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"チュートリアル <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"設定完了"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"タスクバー表示"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"タスクバー非表示"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ナビゲーション バー"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"常にタスクバーを表示"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"ナビゲーション モードを変更"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"タスクバーの区切り"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"上 / 左に移動"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"下 / 右に移動"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{他 # 件のアプリを表示できます。}other{他 # 件のアプリを表示できます。}}"</string> diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml index d987ac0efa..13232da1cd 100644 --- a/quickstep/res/values-ka/strings.xml +++ b/quickstep/res/values-ka/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"აპის შეთავაზებები ჩართულია"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"აპის შეთავაზებები გათიშულია"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ნაწინასწარმეტყველები აპი: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"შეატრიალეთ მოწყობილობა"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ჟესტებით ნავიგაციის სახელმძღვანელოს დასასრულებლად შეატრიალეთ თქვენი მოწყობილობა"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"გადაფურცლეთ უკიდურესი მარჯვენა ან მარცხენა ბოლოდან"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"გადაფურცლეთ მარჯვენა ან მარცხენა კიდიდან ეკრანის ცენტრისკენ და თითი აუშვით"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"გადაფურცლეთ უკიდურესი მარჯვენა ან მარცხენა ბოლოდან."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"გადაფურცლეთ მარჯვენა ან მარცხენა კიდიდან ეკრანის ცენტრისკენ და თითი აუშვით."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"თქვენ ისწავლეთ მარჯვნიდან გადაფურცვლა უკან დასაბრუნებლად. ახლა კი შეიტყვეთ, როგორ გადართოთ აპები."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"თქვენ შეასრულეთ უკან დაბრუნების ჟესტი"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"არ გადაფურცლოთ ეკრანის ბოლოსთან ახლოს"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"თქვენ შეასრულეთ უკან დაბრუნების ჟესტი."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"არ გადაფურცლოთ ეკრანის ბოლოსთან ახლოს."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"დაბრუნების ჟესტის მგრძნობელობის შესაცვლელად გადადით პარამეტრებზე"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"უკან დასაბრუნებლად გადაფურცლეთ"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ბოლო ეკრანზე დასაბრუნებლად გადაფურცლეთ მარცხენა ან მარჯვენა კიდიდან ეკრანის ცენტრისკენ."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"ბოლო ეკრანზე დასაბრუნებლად ორი თითით გადაფურცლეთ მარცხენა ან მარჯვენა კიდიდან ეკრანის ცენტრისკენ."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"უკან დაბრუნება"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"გადაფურცლეთ მარცხენა ან მარჯვენა ბოლოდან ეკრანის შუისკენ"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"გადაფურცლეთ ეკრანის ქვედა კიდიდან ზემოთ"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"არ დააპაუზოთ თითის აშვებამდე"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"გადაფურცლეთ პირდაპირ ზემოთ"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"თქვენ შეასრულეთ მთავარ ეკრანზე დაბრუნების ჟესტი. ახლა კი შევიტყოთ, თუ როგორ დავბრუნდეთ უკან."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"თქვენ შეასრულეთ მთავარ ეკრანზე დაბრუნების ჟესტი"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"გადაფურცლეთ ეკრანის ქვედა კიდიდან ზემოთ."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"არ დააპაუზოთ თითის აშვებამდე."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"გადაფურცლეთ ზემოთ."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"თქვენ შეასრულეთ მთავარ ეკრანზე დაბრუნების ჟესტი. ახლა კი შევიტყოთ, თუ როგორ დავბრუნდეთ უკან."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"თქვენ შეასრულეთ მთავარ ეკრანზე დაბრუნების ჟესტი."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"მთავარი გვერდის სანახავად გადაფურცლეთ"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"გადაფურცლეთ ეკრანის ქვედა კიდიდან ზემოთ. ამ ჟესტს ყოველთვის მთავარი გვერდის ეკრანზე გადაყავხართ."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"ორი თითით გადაფურცლეთ ეკრანის ქვედა ნაწილიდან. ეს ჟესტი ყოველთვის მთავარ ეკრანზე გადაგიყვანთ."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"მთავარზე გადასვლა"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"გადაფურცლეთ ზემოთ თქვენი ეკრანის ბოლოდან"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"შესანიშნავია!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"გადაფურცლეთ ეკრანის ქვედა კიდიდან ზემოთ"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"უფრო ხანგრძლივად დააჭირეთ თითი ფანჯარას, შემდეგ აუშვით"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"გადაფურცლეთ პირდაპირ ზემოთ და შემდეგ დააპაუზეთ"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"ნებისმიერ დროს მთავარ ეკრანზე გადასასვლელად, გადაფურცლეთ ეკრანის ქვემოდან ზემოთ"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"გადაფურცლეთ ეკრანის ქვედა კიდიდან ზემოთ."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"უფრო ხანგრძლივად დააჭირეთ თითი ფანჯარას, რომ არ დაიხუროს."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"გადაფურცლეთ პირდაპირ ზემოთ და შემდეგ დააპაუზეთ."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"თქვენ ისწავლეთ ჟესტების გამოყენება. ჟესტების გამოსართავად გადადით პარამეტრებში."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"თქვენ შეასრულეთ აპების გადართვის ჟესტი"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"თქვენ შეასრულეთ აპების გადართვის ჟესტი."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"აპების გადასართავად გადაფურცლეთ"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"აპების გადასართავად, გადაფურცლეთ ეკრანის ქვედა კიდიდან ზემოთ, დააყოვნეთ, შემდეგ თითი აუშვით."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"აპებს შორის გადასართავად ეკრანის ქვედა კიდიდან ორი თითით გადაფურცლეთ, დააყოვნეთ და აუშვით."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"აპების გადართვა"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"გადაფურცლეთ ეკრანის ქვედა კიდიდან ზემოთ, დააყოვნეთ, შემდეგ აუშვით"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"ყოჩაღ!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"მზად არის"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"მზადაა"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"პარამეტრები"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ამოცანათა ზოლი ნაჩვენებია"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ამოცანათა ზოლი დამალულია"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ნავიგაციის ზოლი"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"ამოცანათა ზოლის მუდამ ჩვენება"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"შეცვალეთ ნავიგაციის რეჟიმი"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ამოცანათა ზოლის გამყოფი"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ზემოთ/მარცხნივ გადატანა"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ქვემოთ/მარჯვნივ გადატანა"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{#-ით მეტი აპის ჩენება}other{#-ით მეტი აპის ჩვენება.}}"</string> diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml index 8a9f94ea61..9f006a9eeb 100644 --- a/quickstep/res/values-kk/strings.xml +++ b/quickstep/res/values-kk/strings.xml @@ -44,52 +44,46 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"\"Қолданба ұсыныстары\" функциясы қосылды."</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"\"Қолданба ұсыныстары\" функциясы өшірулі."</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Болжалды қолданба: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Құрылғыны бұрыңыз"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Қимылмен басқару нұсқаулығын аяқтау үшін құрылғыны бұрыңыз."</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Экранның оң немесе сол жиегінен сырғытыңыз."</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Экранның оң немесе сол жиегінен ортасына қарай сырғытып, саусағыңызды жіберіңіз."</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Экранның оң немесе сол жиегінен сырғытыңыз."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Экранның оң немесе сол жиегінен ортасына қарай сырғытып, саусағыңызды жіберіңіз."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Оңнан солға сырғыту арқылы артқа қайтуды үйрендіңіз. Енді қолданбаларды ауыстыруды үйреніңіз."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Артқа қайту қимылын аяқтадыңыз."</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Сырғытқанда саусақты экранның төменгі жағына қатты жақындатпаңыз."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Артқа қайту қимылын аяқтадыңыз."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Саусағыңызбен сырғыту кезінде экранның төменгі жағына тым жақындамаңыз."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Артқа қайту қимылы сезгіштігін параметрлерден өзгертіңіз."</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Артқа қайту үшін сырғытыңыз"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Соңғы ашылған экранға оралу үшін экранның сол немесе оң жақ шетінен ортасына қарай сырғытыңыз."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Соңғы ашылған экранға оралу үшін екі саусақпен экранның сол не оң жағынан ортасына сырғытыңыз."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Артқа"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Экранның сол немесе оң жақ шетінен ортасына қарай сырғытыңыз."</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Экранның төменгі шетінен жоғары қарай сырғытыңыз."</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Жіберер алдында кідіріс жасамаңыз."</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Тігінен жоғары қарай сырғытыңыз."</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Негізгі экранға қайту қимылын аяқтадыңыз. Енді артқа қайтуды үйреніңіз."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Негізгі экранға қайту қимылын аяқтадыңыз."</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Экранның төменгі шетінен жоғары қарай сырғытыңыз."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Жіберер алдында кідіріс жасамаңыз."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Тігінен жоғары қарай сырғытыңыз."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Негізгі экранға қайту қимылын аяқтадыңыз. Енді артқа қайтуды үйреніңіз."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Негізгі экранға қайту қимылын аяқтадыңыз."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Негізгі экранға өту үшін сырғытыңыз"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Экранның төменгі жағынан жоғары қарай сырғытыңыз. Сонда негізгі экран ашылады."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Екі саусақпен экранның төменгі жағынан жоғары сырғытыңыз. Бұл қимыл үнемі негізгі экранды ашады."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Негізгі экранға өту"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Экранның төменгі жағынан жоғары қарай сырғытыңыз."</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Жарайсыз!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Экранның төменгі шетінен жоғары қарай сырғытыңыз."</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Жіберер алдында терезені ұзағырақ ұстап тұруға тырысыңыз."</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Тігінен жоғары қарай сырғытыңыз да, кідіріңіз."</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Негізгі бетке өту"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Кез келген уақытта негізгі экранға өту үшін экранның астыңғы жағынан жоғары қарай сырғытыңыз."</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Экранның төменгі шетінен жоғары қарай сырғытыңыз."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Жіберер алдында терезені ұзағырақ ұстап тұруға тырысыңыз."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Тігінен жоғары қарай сырғытыңыз да, кідіріңіз."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Қимылдарды қолдануды үйрендіңіз. Қимылдарды өшіру үшін \"Параметрлер\" бөліміне өтіңіз."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Қолданбаларды ауыстыру қимылын аяқтадыңыз."</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Қолданбаларды ауыстыру қимылын аяқтадыңыз."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Қолданбаларды ауыстыру үшін сырғытыңыз"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Бір қолданбадан екіншісіне ауысу үшін экранның төменгі жағынан жоғары қарай сырғытып, ұстап тұрыңыз, кейін жіберіңіз."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Бір қолданбадан екіншісіне ауысу үшін экранның төменгі жағынан жоғары қарай сырғытып, ұстап тұрып жіберіңіз."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Бір қолданбадан екіншісіне ауысу үшін екі саусақпен экранның төменгі жағынан жоғары қарай сырғытып, ұстап тұрып жіберіңіз."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Қолданбалар арасында ауысу"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Экранның төменгі жағынан жоғары қарай сырғытып, ұстап тұрыңыз да, жіберіңіз."</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Жарайсыз!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Бәрі дайын"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Дайын"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Параметрлер"</string> - <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Қайталап көріңіз"</string> + <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Қайталау"</string> <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Жақсы!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"Оқулық: <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"Бәрі дайын!"</string> <string name="allset_hint" msgid="459504134589971527">"Негізгі экранға өту үшін жоғары қарай сырғытыңыз."</string> <string name="allset_button_hint" msgid="2395219947744706291">"Негізгі экранға өту үшін негізгі экран түймесін түртіңіз."</string> <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> пайдалануға дайын."</string> - <string name="default_device_name" msgid="6660656727127422487">"Құрылғы"</string> + <string name="default_device_name" msgid="6660656727127422487">"құрылғы"</string> <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Навигацияның жүйелік параметрлері"</annotation></string> <string name="action_share" msgid="2648470652637092375">"Бөлісу"</string> <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Тапсырмалар жолағы көрсетілді"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Тапсырмалар жолағы жасырылды"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Навигация жолағы"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Тапсырма жолағын үнемі көрсету"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Навигация режимін өзгерту"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Тапсырмалар жолағын бөлгіш"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Жоғары/солға жылжыту"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Төмен/оңға жылжыту"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Тағы # қолданбаны көрсету.}other{Тағы # қолданбаны көрсету.}}"</string> diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml index 0b12d799d5..738e454469 100644 --- a/quickstep/res/values-km/strings.xml +++ b/quickstep/res/values-km/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"បានបើកការណែនាំកម្មវិធី"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"បានបិទការណែនាំកម្មវិធី"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"កម្មវិធីដែលបានព្យាករ៖ <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"បង្វិលឧបករណ៍របស់អ្នក"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"សូមបង្វិលឧបករណ៍របស់អ្នក ដើម្បីបញ្ចប់មេរៀនអំពីការរុករកដោយប្រើចលនា"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ត្រូវប្រាកដថាអ្នកអូសពីគែមខាងស្ដាំ ឬខាងឆ្វេង"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"ត្រូវប្រាកដថាអ្នកអូសពីគែមខាងស្ដាំ ឬខាងឆ្វេងទៅផ្នែកកណ្ដាលនៃអេក្រង់ រួចដកដៃ"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"ត្រូវប្រាកដថាអ្នកអូសពីគែមខាងស្ដាំ ឬខាងឆ្វេង។"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"ត្រូវប្រាកដថាអ្នកអូសពីគែមខាងស្ដាំ ឬខាងឆ្វេងទៅផ្នែកកណ្ដាលនៃអេក្រង់ រួចដកដៃ។"</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"អ្នកបានស្វែងយល់ពីរបៀបអូសពីខាងស្ដាំ ដើម្បីថយក្រោយ។ បន្ទាប់ទៀត សូមស្វែងយល់ពីរបៀបប្ដូរកម្មវិធី។"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"អ្នកបានបញ្ចប់ចលនាថយក្រោយហើយ"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"ត្រូវប្រាកដថាអ្នកមិនអូសទៅជិតផ្នែកខាងក្រោមនៃអេក្រង់ពេក"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"អ្នកបានបញ្ចប់ចលនាថយក្រោយហើយ។"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ត្រូវប្រាកដថាអ្នកមិនអូសទៅជិតផ្នែកខាងក្រោមនៃអេក្រង់ពេក។"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ដើម្បីប្ដូរកម្រិតរំញោចនឹងចលនាថយក្រោយ សូមចូលទៅកាន់ការកំណត់"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"អូសដើម្បីថយក្រោយ"</string> - <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ដើម្បីត្រឡប់ទៅអេក្រង់មុនវិញ សូមអូសពីគែមខាងឆ្វេង ឬខាងស្ដាំទៅផ្នែកកណ្ដាលនៃអេក្រង់។"</string> + <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ដើម្បីត្រឡប់ទៅអេក្រង់ចុងក្រោយវិញ សូមអូសពីគែមខាងឆ្វេង ឬខាងស្ដាំទៅផ្នែកកណ្ដាលនៃអេក្រង់។"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"ដើម្បីត្រឡប់ទៅអេក្រង់ចុងក្រោយវិញ អូសដោយប្រើម្រាមដៃពីរពីគែមខាងឆ្វេង ឬខាងស្ដាំទៅផ្នែកកណ្ដាលនៃអេក្រង់។"</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"ថយក្រោយ"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"អូសពីគែមខាងឆ្វេង ឬខាងស្ដាំទៅផ្នែកកណ្ដាលនៃអេក្រង់"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"ត្រូវប្រាកដថាអ្នកអូសឡើងលើពីគែមខាងក្រោមនៃអេក្រង់"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"ត្រូវប្រាកដថាអ្នកមិនផ្អាក មុនពេលដកដៃ"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"ត្រូវប្រាកដថាអ្នកអូសត្រង់ឡើងលើ"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"អ្នកបានបញ្ចប់ចលនាចូលទៅកាន់ទំព័រដើមហើយ។ បន្ទាប់មកទៀត សូមស្វែងយល់ពីរបៀបថយក្រោយ។"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"អ្នកបានបញ្ចប់ចលនាចូលទៅកាន់ទំព័រដើមហើយ"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ត្រូវប្រាកដថាអ្នកអូសឡើងលើពីគែមខាងក្រោមនៃអេក្រង់។"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ត្រូវប្រាកដថាអ្នកមិនផ្អាក មុនពេលដកដៃ។"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ត្រូវប្រាកដថាអ្នកអូសត្រង់ឡើងលើ។"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"អ្នកបានបញ្ចប់ចលនាចូលទៅកាន់ទំព័រដើមហើយ។ បន្ទាប់មកទៀត សូមស្វែងយល់ពីរបៀបថយក្រោយ។"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"អ្នកបានបញ្ចប់ចលនាចូលទៅកាន់ទំព័រដើមហើយ។"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"អូសដើម្បីចូលទៅកាន់អេក្រង់ដើម"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"អូសឡើងលើពីផ្នែកខាងក្រោមនៃអេក្រង់របស់អ្នក។ ចលនានេះនាំអ្នកទៅអេក្រង់ដើមជានិច្ច។"</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"អូសឡើងលើដោយប្រើម្រាមដៃពីរពីផ្នែកខាងក្រោមនៃអេក្រង់។ ចលនានេះតែងតែនាំអ្នកទៅអេក្រង់ដើមជានិច្ច។"</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ទៅអេក្រង់ដើម"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"អូសឡើងលើពីផ្នែកខាងក្រោមនៃអេក្រង់របស់អ្នក"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"ធ្វើបានល្អ!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"ត្រូវប្រាកដថាអ្នកអូសឡើងលើពីគែមខាងក្រោមនៃអេក្រង់"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"សាកល្បងសង្កត់វិនដូឱ្យបានយូរជាងនេះ មុនពេលដកដៃ"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"ត្រូវប្រាកដថាអ្នកអូសត្រង់ឡើងលើ រួចផ្អាក"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ទៅទំព័រដើម"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"ដើម្បីទៅកាន់អេក្រង់ដើមរបស់អ្នកនៅពេលណាក៏បាន សូមអូសឡើងលើពីផ្នែកខាងក្រោមនៃអេក្រង់របស់អ្នក"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ត្រូវប្រាកដថាអ្នកអូសឡើងលើពីគែមខាងក្រោមនៃអេក្រង់។"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"សាកល្បងសង្កត់វិនដូឱ្យបានយូរជាងនេះ មុនពេលដកដៃ។"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ត្រូវប្រាកដថាអ្នកអូសត្រង់ឡើងលើ រួចផ្អាក។"</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"អ្នកបានស្វែងយល់អំពីរបៀបប្រើចលនាហើយ។ ដើម្បីបិទចលនា សូមចូលទៅកាន់ការកំណត់។"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"អ្នកបានបញ្ចប់ចលនាប្ដូរកម្មវិធីហើយ"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"អ្នកបានបញ្ចប់ចលនាប្ដូរកម្មវិធីហើយ។"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"អូសដើម្បីប្ដូរកម្មវិធី"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ដើម្បីប្ដូររវាងកម្មវិធី សូមអូសឡើងលើពីផ្នែកខាងក្រោមនៃអេក្រង់របស់អ្នក រួចចុចឱ្យជាប់ បន្ទាប់មកដកដៃចេញ។"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"ដើម្បីប្ដូរកម្មវិធី អូសឡើងលើដោយប្រើម្រាមដៃពីរពីផ្នែកខាងក្រោមនៃអេក្រង់របស់អ្នក សង្កត់ ហើយលែងវិញ។"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ប្ដូរកម្មវិធី"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"អូសឡើងលើពីផ្នែកខាងក្រោមនៃអេក្រង់របស់អ្នក រួចចុចឱ្យជាប់ បន្ទាប់មកដកដៃចេញ"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"ល្អណាស់!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"រួចហើយ"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"រួចរាល់"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ការកំណត់"</string> @@ -88,7 +82,7 @@ <string name="allset_title" msgid="5021126669778966707">"រួចហើយ!"</string> <string name="allset_hint" msgid="459504134589971527">"អូសឡើងលើ ដើម្បីចូលទៅកាន់អេក្រង់ដើម"</string> <string name="allset_button_hint" msgid="2395219947744706291">"ចុចប៊ូតុងដើម ដើម្បីចូលទៅកាន់អេក្រង់ដើមរបស់អ្នក"</string> - <string name="allset_description_generic" msgid="5385500062202019855">"អ្នកអាចចាប់ផ្ដើមប្រើ<xliff:g id="DEVICE">%1$s</xliff:g>របស់អ្នកបានហើយ"</string> + <string name="allset_description_generic" msgid="5385500062202019855">"អ្នកអាចចាប់ផ្ដើមប្រើ <xliff:g id="DEVICE">%1$s</xliff:g> របស់អ្នកបានហើយ"</string> <string name="default_device_name" msgid="6660656727127422487">"ឧបករណ៍"</string> <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ការកំណត់ការរុករកប្រព័ន្ធ"</annotation></string> <string name="action_share" msgid="2648470652637092375">"ចែករំលែក"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"បានបង្ហាញរបារកិច្ចការ"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"បានលាក់របារកិច្ចការ"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"របាររុករក"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"បង្ហាញរបារកិច្ចការជានិច្ច"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"ប្ដូរមុខងាររុករក"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"បន្ទាត់ខណ្ឌចែករបារកិច្ចការ"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ផ្លាស់ទីទៅខាងលើ/ឆ្វេង"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ផ្លាស់ទីទៅខាងក្រោម/ស្ដាំ"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{បង្ហាញកម្មវិធី # ទៀត។}other{បង្ហាញកម្មវិធី # ទៀត។}}"</string> diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml index 2666846fe4..37e0acdf15 100644 --- a/quickstep/res/values-kn/strings.xml +++ b/quickstep/res/values-kn/strings.xml @@ -44,50 +44,44 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ಶಿಫಾರಸು ಮಾಡಿದ ಆ್ಯಪ್: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"ನಿಮ್ಮ ಸಾಧನವನ್ನು ತಿರುಗಿಸಿ"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ಗೆಸ್ಚರ್ ನ್ಯಾವಿಗೇಶನ್ ಟುಟೋರಿಯಲ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಲು, ನಿಮ್ಮ ಸಾಧನವನ್ನು ತಿರುಗಿಸಿ"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ನೀವು ಬಲಕೊನೆಯ ಅಂಚಿನಿಂದ ಅಥವಾ ಎಡಕೊನೆಯ ಅಂಚಿನಿಂದ ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"ನೀವು ಬಲ ಅಥವಾ ಎಡ ಅಂಚಿನಿಂದ ಸ್ಕ್ರೀನ್ನ ಮಧ್ಯಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಂಡು ಬಿಟ್ಟುಬಿಡಿ"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"ನೀವು ಬಲಕೊನೆಯ ಅಂಚಿನಿಂದ ಅಥವಾ ಎಡಕೊನೆಯ ಅಂಚಿನಿಂದ ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"ನೀವು ಬಲ ಅಥವಾ ಎಡ ಅಂಚಿನಿಂದ ಸ್ಕ್ರೀನ್ನ ಮಧ್ಯಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಂಡು ಬಿಟ್ಟುಬಿಡಿ."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"ಹಿಂದೆ ಹೋಗಲು ಬಲದಿಂದ ಸ್ವೈಪ್ ಮಾಡುವುದು ಹೇಗೆಂದು ಕಲಿತಿರಿ. ಮುಂದೆ, ಆ್ಯಪ್ಗಳನ್ನು ಬದಲಿಸುವುದು ಹೇಗೆಂದು ತಿಳಿಯಿರಿ."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"ನೀವು ಗೋ ಬ್ಯಾಕ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"ನೀವು ಸ್ಕ್ರೀನ್ನ ಕೆಳಭಾಗಕ್ಕೆ ಹೆಚ್ಚು ಹತ್ತಿರ ಸ್ವೈಪ್ ಮಾಡದಂತೆ ನೋಡಿಕೊಳ್ಳಿ"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"ನೀವು ಗೋ ಬ್ಯಾಕ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ನೀವು ಸ್ಕ್ರೀನ್ನ ಕೆಳಭಾಗಕ್ಕೆ ಹೆಚ್ಚು ಹತ್ತಿರ ಸ್ವೈಪ್ ಮಾಡದಂತೆ ನೋಡಿಕೊಳ್ಳಿ."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ಬ್ಯಾಕ್ ಗೆಸ್ಚರ್ನ ಸೂಕ್ಷ್ಮತೆ ಬದಲಾಯಿಸಲು, ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ಹೋಗಿ"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"ಹಿಂದಕ್ಕೆ ಹೋಗಲು ಸ್ವೈಪ್ ಮಾಡಿ"</string> - <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ಹಿಂದಿನ ಸ್ಕ್ರೀನ್ಗೆ ಮರಳಲು, ಎಡ ಅಥವಾ ಬಲ ಅಂಚಿನಿಂದ ಸ್ಕ್ರೀನ್ ಮಧ್ಯದವರೆಗೆ ಸ್ವೈಪ್ ಮಾಡಿ."</string> + <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ಕೊನೆಯ ಸ್ಕ್ರೀನ್ಗೆ ಹಿಂತಿರುಗಲು, ಎಡ ಅಥವಾ ಬಲ ಅಂಚಿನಿಂದ ಸ್ಕ್ರೀನ್ ಮಧ್ಯಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"ಹಿಂದಿನ ಸ್ಕ್ರೀನ್ಗೆ ಹೋಗಲು, 2 ಬೆರಳುಗಳಿಂದ ಎಡ ಅಥವಾ ಬಲ ಅಂಚಿನಿಂದ ಸ್ಕ್ರೀನ್ ಮಧ್ಯಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"ಹಿಂದಿರುಗಿ"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"ಎಡ ಅಥವಾ ಬಲ ಅಂಚಿನಿಂದ ಸ್ಕ್ರೀನ್ನ ಮಧ್ಯಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"ಸ್ಕ್ರೀನ್ನ ಕೆಳಗಿನ ಅಂಚಿನಿಂದ ನೀವು ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"ವಿರಾಮಗೊಳಿಸದೆ ನಿಮ್ಮ ಬೆರಳನ್ನು ಸ್ಕ್ರೀನ್ನಿಂದ ಮೇಲೆತ್ತಿ"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"ನೀವು ನೇರವಾಗಿ ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"ನೀವು ಹೋಮ್ಗೆ ಹೋಗಿ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ. ಮುಂದೆ, ಹಿಂದಕ್ಕೆ ಹೋಗುವುದು ಹೇಗೆ ಎಂದು ತಿಳಿಯಿರಿ."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"ನೀವು ಹೋಮ್ಗೆ ಹೋಗಿ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ಸ್ಕ್ರೀನ್ನ ಕೆಳಗಿನ ಅಂಚಿನಿಂದ ನೀವು ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ವಿರಾಮಗೊಳಿಸದೆ ನಿಮ್ಮ ಬೆರಳನ್ನು ಸ್ಕ್ರೀನ್ನಿಂದ ಮೇಲೆತ್ತಿ."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ನೀವು ನೇರವಾಗಿ ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"ನೀವು ಗೋ ಹೋಮ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ. ಮುಂದೆ, ಹಿಂದಕ್ಕೆ ಹೋಗುವುದು ಹೇಗೆ ಎಂದು ತಿಳಿಯಿರಿ."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"ನೀವು ಗೋ ಹೋಮ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಹಿಂತಿರುಗಲು ಸ್ವೈಪ್ ಮಾಡಿ"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ಸ್ಕ್ರೀನ್ನ ಕೆಳಗಿನಿಂದ ಮೇಲೆ ಸ್ವೈಪ್ ಮಾಡಿ. ಈ ಗೆಸ್ಚರ್ ಯಾವಾಗಲೂ ನಿಮ್ಮನ್ನು ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಕರೆದೊಯ್ಯುತ್ತದೆ."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"2 ಬೆರಳುಗಳಿಂದ ಸ್ಕ್ರೀನ್ನ ಕೆಳಗಿನಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ. ಈ ಗೆಸ್ಚರ್ ಯಾವಾಗಲೂ ನಿಮ್ಮನ್ನು ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಕರೆದೊಯ್ಯುತ್ತದೆ."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ಹೋಮ್ಗೆ ಹೋಗಿ"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನ ಕೆಳಗಿನಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"ಭೇಷ್!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"ಸ್ಕ್ರೀನ್ನ ಕೆಳಗಿನ ಅಂಚಿನಿಂದ ನೀವು ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"ಬೆರಳನ್ನು ಮೇಲೆತ್ತುವ ಮೊದಲು ವಿಂಡೋವನ್ನು ಹೆಚ್ಚು ಸಮಯ ಹಿಡಿದಿಡಲು ಪ್ರಯತ್ನಿಸಿ"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"ನೀವು ನೇರವಾಗಿ ಸ್ವೈಪ್ ಮಾಡಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ, ನಂತರ ವಿರಾಮಗೊಳಿಸಿ"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ಮುಖಪುಟಕ್ಕೆ ಹೋಗಿ"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಹೋಗಲು, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನ ಕೆಳಭಾಗದಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ಸ್ಕ್ರೀನ್ನ ಕೆಳಗಿನ ಅಂಚಿನಿಂದ ನೀವು ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"ಬೆರಳನ್ನು ಮೇಲೆತ್ತುವ ಮೊದಲು ವಿಂಡೋವನ್ನು ಹೆಚ್ಚು ಸಮಯ ಹಿಡಿದಿಡಲು ಪ್ರಯತ್ನಿಸಿ."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ನೀವು ನೇರವಾಗಿ ಸ್ವೈಪ್ ಮಾಡಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ, ನಂತರ ವಿರಾಮಗೊಳಿಸಿ."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"ಗೆಶ್ಚರ್ಗಳನ್ನು ಬಳಸುವುದು ಹೇಗೆಂದು ನೀವು ತಿಳಿದುಕೊಂಡಿರುವಿರಿ. ಗೆಶ್ಚರ್ಗಳನ್ನು ಆಫ್ ಮಾಡಲು, ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ಹೋಗಿ."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"ನೀವು ಆ್ಯಪ್ಗಳನ್ನು ಬದಲಾಯಿಸುವ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"ನೀವು ಆ್ಯಪ್ಗಳನ್ನು ಬದಲಾಯಿಸುವ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ಆ್ಯಪ್ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಸ್ವೈಪ್ ಮಾಡಿ"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ಆ್ಯಪ್ಗಳ ನಡುವೆ ಬದಲಿಸಲು, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನ ಕೆಳಭಾಗದಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ, ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಿ, ನಂತರ ಬಿಟ್ಟುಬಿಡಿ."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"ಆ್ಯಪ್ಗಳ ನಡುವೆ ಬದಲಿಸಲು, 2 ಬೆರಳುಗಳಿಂದ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನ ಕೆಳಭಾಗದಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ, ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಿ, ನಂತರ ಬಿಟ್ಟುಬಿಡಿ."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ಆ್ಯಪ್ಗಳನ್ನು ಬದಲಿಸಿ"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನ ಕೆಳಭಾಗದಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ, ಹೋಲ್ಡ್ ಮಾಡಿ, ನಂತರ ಬಿಡುಗಡೆ ಮಾಡಿ"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"ಭೇಷ್!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"ಸಂಪೂರ್ಣ ಸಿದ್ಧವಾಗಿದೆ"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"ಮುಗಿದಿದೆ"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> <string name="gesture_tutorial_try_again" msgid="65962545858556697">"ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string> - <string name="gesture_tutorial_nice" msgid="2936275692616928280">"ಭೇಷ್!"</string> + <string name="gesture_tutorial_nice" msgid="2936275692616928280">"ಚೆನ್ನಾಗಿದೆ!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"ಟ್ಯುಟೋರಿಯಲ್ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"ಎಲ್ಲವೂ ಸಿದ್ಧವಾಗಿದೆ!"</string> <string name="allset_hint" msgid="459504134589971527">"ಮುಖಪುಟಕ್ಕೆ ಹೋಗಲು ಮೇಲೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string> - <string name="allset_button_hint" msgid="2395219947744706291">"ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಹೋಗಲು ಹೋಮ್ ಬಟನ್ ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> + <string name="allset_button_hint" msgid="2395219947744706291">"ನಿಮ್ಮ ಮುಖಪುಟದ ಪರದೆಗೆ ಹೋಗಲು ಮುಖಪುಟ ಬಟನ್ ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="allset_description_generic" msgid="5385500062202019855">"ನಿಮ್ಮ <xliff:g id="DEVICE">%1$s</xliff:g> ಬಳಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸಲು ನೀವು ಸಿದ್ಧರಾಗಿರುವಿರಿ"</string> <string name="default_device_name" msgid="6660656727127422487">"ಸಾಧನ"</string> <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ಸಿಸ್ಟಂ ನ್ಯಾವಿಗೇಶನ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</annotation></string> @@ -109,21 +103,18 @@ <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"ಟಾಸ್ಕ್ಬಾರ್ ಅನ್ನು ಸ್ವಯಂ-ಮರೆಮಾಡಲು ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಗೆಸ್ಚರ್ ನ್ಯಾವಿಗೇಶನ್ ಅನ್ನು ಆನ್ ಮಾಡಿ"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"ಟಾಸ್ಕ್ಬಾರ್ ಮೂಲಕ ಹೆಚ್ಚಿನದನ್ನು ಮಾಡಿ"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"ಮುಚ್ಚಿರಿ"</string> - <string name="taskbar_edu_done" msgid="6880178093977704569">"ಆಯಿತು"</string> + <string name="taskbar_edu_done" msgid="6880178093977704569">"ಮುಗಿದಿದೆ"</string> <string name="taskbar_button_home" msgid="2151398979630664652">"ಮುಖಪುಟ"</string> <string name="taskbar_button_a11y" msgid="5241161324875094465">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ"</string> <string name="taskbar_button_back" msgid="8558862226461164514">"ಹಿಂದೆ"</string> <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME ಪರಿವರ್ತಕ"</string> <string name="taskbar_button_recents" msgid="7273376136216613134">"ಇತ್ತೀಚಿನವು"</string> - <string name="taskbar_button_notifications" msgid="7471740351507357318">"ನೋಟಿಫಿಕೇಶನ್ಗಳು"</string> + <string name="taskbar_button_notifications" msgid="7471740351507357318">"ಅಧಿಸೂಚನೆಗಳು"</string> <string name="taskbar_button_quick_settings" msgid="227662894293189391">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> <string name="taskbar_a11y_title" msgid="6432169809852243110">"ಟಾಸ್ಕ್ಬಾರ್"</string> <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ಟಾಸ್ಕ್ಬಾರ್ ತೋರಿಸಲಾಗಿದೆ"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ಟಾಸ್ಕ್ಬಾರ್ ಮರೆಮಾಡಲಾಗಿದೆ"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ನ್ಯಾವಿಗೇಷನ್ ಬಾರ್"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"ಯಾವಾಗಲೂ ಟಾಸ್ಕ್ಬಾರ್ ತೋರಿಸಿ"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"ನ್ಯಾವಿಗೇಶನ್ ಮೋಡ್ ಬದಲಾಯಿಸಿ"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ಟಾಸ್ಕ್ಬಾರ್ ಡಿವೈಡರ್"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ಮೇಲಿನ/ಎಡಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ಕೆಳಗಿನ/ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{ಇನ್ನೂ # ಆ್ಯಪ್ ಅನ್ನು ತೋರಿಸಿ.}one{ಇನ್ನೂ # ಆ್ಯಪ್ಗಳನ್ನು ತೋರಿಸಿ.}other{ಇನ್ನೂ # ಆ್ಯಪ್ಗಳನ್ನು ತೋರಿಸಿ.}}"</string> diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml index 8a4c9b409c..cff87a13b9 100644 --- a/quickstep/res/values-ko/strings.xml +++ b/quickstep/res/values-ko/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"앱 제안이 사용 설정됨"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"앱 제안이 사용 중지됨"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"예상 앱: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"기기를 회전시키세요"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"동작 탐색 튜토리얼을 완료하려면 기기를 회전시키세요."</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"오른쪽 또는 왼쪽 가장자리 끝에서 스와이프하세요."</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"오른쪽 또는 왼쪽 가장자리에서 화면 중앙으로 스와이프한 후 손가락을 떼세요."</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"오른쪽 또는 왼쪽 가장자리 끝에서 스와이프하세요."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"오른쪽 또는 왼쪽 가장자리에서 화면 중앙으로 스와이프한 후 손가락을 떼세요."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"오른쪽에서 스와이프하여 뒤로 돌아가는 방법을 배웠습니다. 이번에는 앱 전환 방법을 알아보겠습니다."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"돌아가기 동작을 완료했습니다."</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"화면 하단에 지나치게 가까운 곳에서 스와이프하면 안 됩니다."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"돌아가기 동작을 완료했습니다."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"화면 하단에 지나치게 가까운 곳에서 스와이프하면 안 됩니다."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"돌아가기 동작의 민감도를 변경하려면 설정으로 이동하세요"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"스와이프하여 돌아가기"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"이전 화면으로 돌아가려면 왼쪽 또는 오른쪽 가장자리에서 화면 중앙으로 스와이프하세요."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"마지막 화면으로 돌아가려면 두 손가락을 사용해 왼쪽 또는 오른쪽 가장자리에서 화면 중앙으로 스와이프하세요"</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"뒤로"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"왼쪽 또는 오른쪽 가장자리에서 화면 중앙으로 스와이프하세요."</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"화면 하단 가장자리에서 위로 스와이프하세요."</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"손가락을 떼기 전에 멈추지 않아야 합니다."</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"위로 곧게 스와이프하세요."</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"홈으로 이동 동작을 완료했습니다. 이번에는 뒤로 돌아가는 방법을 알아보겠습니다."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"홈으로 이동 동작을 완료했습니다."</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"화면 하단 가장자리에서 위로 스와이프하세요."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"손가락을 떼기 전에 멈추지 않아야 합니다."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"위로 곧게 스와이프하세요."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"홈으로 이동 동작을 완료했습니다. 이번에는 뒤로 돌아가는 방법을 알아보겠습니다."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"홈으로 이동 동작을 완료했습니다."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"스와이프하여 홈으로 이동"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"화면 하단에서 위로 스와이프합니다. 이 동작을 사용하면 언제든지 홈 화면으로 이동할 수 있습니다."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"두 손가락을 사용해 화면 하단에서 위로 스와이프하세요. 이 동작을 사용하면 언제든지 홈 화면으로 이동할 수 있습니다"</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"홈으로 이동"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"화면 하단에서 위로 스와이프하세요."</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"아주 좋습니다"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"화면 하단 가장자리에서 위로 스와이프하세요."</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"창을 더 오래 누르고 있다가 손가락을 떼 보세요."</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"위로 곧게 스와이프한 후 잠시 멈추세요."</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"언제든지 화면을 아래에서 위로 스와이프하여 홈 화면으로 이동할 수 있습니다"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"화면 하단 가장자리에서 위로 스와이프하세요."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"창을 더 오래 누르고 있다가 손가락을 떼 보세요."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"위로 곧게 스와이프한 후 잠시 멈추세요."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"동작 사용 방법을 알아봤습니다. 동작을 사용 중지하려면 설정으로 이동하세요."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"앱 전환 동작을 완료했습니다."</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"앱 전환 동작을 완료했습니다."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"스와이프하여 앱 전환"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"앱 간에 전환하려면 화면 하단에서 위로 스와이프하고 잠시 멈춘 다음 손가락을 떼세요."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"앱 간에 전환하려면 두 손가락을 사용해 화면 하단에서 위로 스와이프하고 잠시 멈춘 다음 손가락을 떼세요"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"앱 전환"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"화면 하단에서 위로 스와이프하고 잠시 멈춘 다음 손가락을 떼세요."</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"잘하셨습니다"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"설정 완료"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"완료"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"설정"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"태스크 바 표시"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"태스크 바 숨김"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"탐색 메뉴"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"태스크 바 항상 표시"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"탐색 모드 변경"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"태스크 바 분할"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"상단/왼쪽으로 이동"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"하단/오른쪽으로 이동"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{앱 #개 더 표시}other{앱 #개 더 표시}}"</string> diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml index 7c74fa6c9a..4cb31fddf5 100644 --- a/quickstep/res/values-ky/strings.xml +++ b/quickstep/res/values-ky/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Сунушталган колдонмолор функциясы иштетилди"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Сунушталган колдонмолор функциясы өчүрүлгөн"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Божомолдонгон колдонмо: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Түзмөгүңүздү буруңуз"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Жаңсап чабыттоо үйрөткүчүнүн аягына чыгуу үчүн түзмөктү буруңуз"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Экранды эң четинен оңдон солго же солдон оңго карай сүрүңүз"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Экранды оң же сол жагынан ортосуна карай сүрүп, манжаңызды алыңыз"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Экранды эң четинен оңдон солго же солдон оңго карай сүрүңүз."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Экранды оң же сол жагынан ортосуна карай сүрүп, манжаңызды алыңыз."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Артка кайтуу үчүн экранды оңдон солго карай сүрүүнү үйрөндүңүз. Эми колдонмолорду которуштурганды үйрөнүп алыңыз."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"\"Артка\" жаңсоосун үйрөндүңүз"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Манжаңызды экрандын ылдый жагына өтө жакындатпай сүрүңүз"</string> - <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"\"Артка\" жаң-нун сезгичтигин өзгөртүү үчүн параметрлерге өтүңүз"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"\"Артка\" жаңсоосун үйрөндүңүз."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Манжаңызды экрандын ылдый жагына өтө жакындатпай сүрүңүз."</string> + <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"\"Артка\" жаң-нун сезгичтигин өзгөртүү үчүн жөндөөлөргө өтүңүз"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Артка кайтуу үчүн сүрүңүз"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Акыркы экранга кайтуу үчүн экранды сол же оң жагынан ортосуна карай сүрүңүз."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Акыркы экранга кайтуу үчүн экранды сол же оң жагынан ортосуна карай 2 манжаңыз менен сүрүңүз."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Артка кайтуу"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Экранды сол же оң жагынан ортосуна карай сүрүңүз"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Экранды ылдыйдан өйдө сүрүңүз"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Манжаңызды алганга чейин токтотпоңуз"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Экранды өйдө сүрүңүз"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"\"Башкы бетке өтүү\" жаңсоосун үйрөндүңүз. Эми артка кайтууну үйрөнүп алыңыз."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"\"Башкы бетке өтүү\" жаңсоосун үйрөндүңүз"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Экранды ылдыйдан өйдө сүрүңүз."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Манжаңызды алганга чейин токтотпоңуз."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Экранды өйдө сүрүңүз."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"\"Башкы бетке өтүү\" жаңсоосун үйрөндүңүз. Эми артка кайтууну үйрөнүп алыңыз."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"\"Башкы бетке өтүү\" жаңсоосун үйрөндүңүз."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Башкы бетке өтүү үчүн сүрүп коюңуз"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Экранды ылдый жагынан өйдө сүрүңүз. Бул жаңсоо сизди ар дайым Башкы экранга алып барат."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Экранды ылдый жагынан өйдө 2 манжаңыз менен сүрүңүз. Бул жаңсоо ар дайым Башкы экранга алып барат."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Башкы бетке өтүү"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Экранды төмөндөн жогору карай сүрүңүз"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Азаматсыз!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Экранды ылдыйдан өйдө сүрүңүз"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Манжаңызды алуудан мурун экранда узагыраак кармаңыз"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Экранды өйдө карай сүрүп, токтоп туруңуз"</string> - <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Жаңсоолорду колдонгонду үйрөндүңүз. Жаңсоолорду өчүрүү үчүн параметрлерге өтүңүз."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"\"Колдонмолорду которуштуруу\" жаңсоосун үйрөндүңүз"</string> - <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Башка колдонмого которулуу үчүн сүрүңүз"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Каалаган убакта башкы экранга өтүү үчүн экранды ылдыйдан жогору карай сүрүңүз"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Экранды ылдыйдан өйдө сүрүңүз."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Манжаңызды алуудан мурун экранда узагыраак кармаңыз."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Экранды өйдө карай сүрүп, токтоп туруңуз."</string> + <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Жаңсоолорду колдонгонду үйрөндүңүз. Жаңсоолорду өчүрүү үчүн жөндөөлөргө өтүңүз."</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"\"Колдонмолорду которуштуруу\" жаңсоосун үйрөндүңүз."</string> + <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Колдонмолорду которуштуруу үчүн сүрүңүз"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Бир колдонмодон экинчисине өтүү үчүн экранды ылдыйдан өйдө карай сүрүп, бир аз коё бербей туруңуз."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Бир колдонмодон экинчисине өтүү үчүн экранды 2 манжа менен ылдыйдан өйдө сүрүп, коё бербей туруңуз."</string> - <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Башка колдонмого которулуу"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Экранды төмөндөн жогору карай сүрүңүз да, бир аз коё бербей кармап туруңуз"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Эң жакшы!"</string> + <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Колдонмолорду которуштуруу"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Дапдаяр!"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Бүттү"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Параметрлер"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Тапшырмалар панели көрсөтүлдү"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Тапшырмалар панели жашырылды"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Чабыттоо тилкеси"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Тапшырмалар панелин ар дайым көрсөтүү"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Өтүү режимин өзгөртүү"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Тапшырмалар панелин бөлгүч"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Жогорку/сол бурчка жылдыруу"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Төмөнкү/оң бурчка жылдыруу"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Дагы # колдонмону көрсөтүү.}other{Дагы # колдонмону көрсөтүү.}}"</string> diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml index 66db658a68..be5d894c06 100644 --- a/quickstep/res/values-lo/strings.xml +++ b/quickstep/res/values-lo/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ເປີດການນຳໃຊ້ການແນະນຳແອັບແລ້ວ"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ປິດການນຳໃຊ້ການແນະນຳແອັບແລ້ວ"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ແອັບທີ່ຄາດເດົາໄວ້: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"ໝຸນອຸປະກອນຂອງທ່ານ"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ກະລຸນາໝຸນອຸປະກອນຂອງທ່ານເພື່ອເຮັດຕາມການສອນການນຳໃຊ້ກ່ຽວກັບການນຳທາງແບບທ່າທາງໃຫ້ສຳເລັດ"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ກະລຸນາກວດສອບວ່າທ່ານປັດຈາກຂອບຂວາສຸດ ຫຼື ຊ້າຍສຸດ"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"ກະລຸນາກວດສອບວ່າທ່ານປັດຈາກຂອບຂວາ ຫຼື ຊ້າຍໄປຫາທາງກາງຂອງໜ້າຈໍແລ້ວປ່ອຍນິ້ວ"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"ກະລຸນາກວດສອບວ່າທ່ານປັດຈາກຂອບຂວາສຸດ ຫຼື ຊ້າຍສຸດ."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"ກະລຸນາກວດສອບວ່າທ່ານປັດຈາກຂອບຂວາ ຫຼື ຊ້າຍໄປຫາທາງກາງຂອງໜ້າຈໍແລ້ວປ່ອຍນິ້ວ."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"ທ່ານຮຽນຮູ້ວິທີປັດຈາກຂວາເພື່ອກັບຄືນແລ້ວ. ຕໍ່ໄປ, ມາສຶກສາວິທີສະຫຼັບແອັບ."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"ທ່ານໃຊ້ທ່າທາງກັບຄືນສຳເລັດແລ້ວ"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"ກະລຸນາກວດສອບວ່າທ່ານບໍ່ໄດ້ປັດໃກ້ກັບທາງລຸ່ມຂອງໜ້າຈໍເກີນໄປ"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"ທ່ານໃຊ້ທ່າທາງກັບຄືນສຳເລັດແລ້ວ."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ກະລຸນາກວດສອບວ່າທ່ານບໍ່ໄດ້ປັດໃກ້ກັບທາງລຸ່ມຂອງໜ້າຈໍເກີນໄປ."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ເພື່ອປ່ຽນຄວາມລະອຽດອ່ອນຂອງທ່າທາງກັບຄືນ, ໃຫ້ໄປຫາການຕັ້ງຄ່າ"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"ປັດເພື່ອກັບຄືນ"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ເພື່ອກັບໄປໜ້າຈໍຫຼ້າສຸດ, ໃຫ້ປັດຈາກຂອບຊ້າຍ ຫຼື ຂວາໄປຫາທາງກາງຂອງໜ້າຈໍ."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"ເພື່ອກັບໄປໜ້າຈໍຫຼ້າສຸດ, ໃຫ້ປັດດ້ວຍ 2 ນິ້ວຈາກຂອບຊ້າຍ ຫຼື ຂວາໄປຫາທາງກາງຂອງໜ້າຈໍ."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"ກັບຄືນ"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"ປັດຈາກຂອບຊ້າຍ ຫຼື ຂວາໄປຫາກາງໜ້າຈໍ"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"ກະລຸນາກວດສອບວ່າທ່ານປັດຂຶ້ນຈາກຂອບລຸ່ມສຸດຂອງໜ້າຈໍ"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"ກະລຸນາກວດສອບວ່າທ່ານບໍ່ຢຸດຊົ່ວຄາວກ່ອນປ່ອຍນິ້ວ"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"ກະລຸນາກວດສອບວ່າທ່ານປັດຂຶ້ນໄປຊື່ໆ"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"ທ່ານໃຊ້ທ່າທາງໄປໜ້າຫຼັກສຳເລັດແລ້ວ. ຕໍ່ໄປ, ມາສຶກສາວິທີກັບຄືນ."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"ທ່ານໃຊ້ທ່າທາງໄປໜ້າຫຼັກສຳເລັດແລ້ວ"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ກະລຸນາກວດສອບວ່າທ່ານປັດຂຶ້ນຈາກຂອບລຸ່ມສຸດຂອງໜ້າຈໍ."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ກະລຸນາກວດສອບວ່າທ່ານບໍ່ຢຸດຊົ່ວຄາວກ່ອນປ່ອຍນິ້ວ."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ກະລຸນາກວດສອບວ່າທ່ານປັດຂຶ້ນໄປຊື່ໆ."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"ທ່ານໃຊ້ທ່າທາງໄປໜ້າຫຼັກສຳເລັດແລ້ວ. ຕໍ່ໄປ, ມາສຶກສາວິທີກັບຄືນ."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"ທ່ານໃຊ້ທ່າທາງໄປໜ້າຫຼັກສຳເລັດແລ້ວ."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"ປັດເພື່ອໄປໜ້າຫຼັກ"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ປັດຂຶ້ນມາຈາກລຸ່ມສຸດຂອງໜ້າຈໍທ່ານ. ທ່າທາງນີ້ຈະພາທ່ານໄປໂຮມສະກຣີນສະເໝີ."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"ປັດຂຶ້ນດ້ວຍ 2 ນິ້ວຈາກລຸ່ມສຸດຂອງໜ້າຈໍ. ທ່າທາງນີ້ຈະພາທ່ານໄປໂຮມສະກຣີນສະເໝີ."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ໄປໜ້າຫຼັກ"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"ປັດຂຶ້ນຈາກລຸ່ມສຸດຂອງໜ້າຈໍທ່ານ"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"ດີຫຼາຍ!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"ກະລຸນາກວດສອບວ່າທ່ານປັດຂຶ້ນຈາກຂອບລຸ່ມສຸດຂອງໜ້າຈໍ"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"ລອງກົດໃສ່ໜ້າຈໍຄ້າງໄວ້ດົນຂຶ້ນກ່ອນປ່ອຍນິ້ວ"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"ກະລຸນາກວດສອບວ່າທ່ານປັດຂຶ້ນຊື່ໆ, ຈາກນັ້ນຢຸດຊົ່ວຄາວ"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"ປັດຂຶ້ນຈາກລຸ່ມສຸດຂອງໜ້າຈໍຂອງທ່ານເພື່ອກັບໄປໂຮມສະກຣີນຂອງທ່ານໄດ້ທຸກເວລາ"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ກະລຸນາກວດສອບວ່າທ່ານປັດຂຶ້ນຈາກຂອບລຸ່ມສຸດຂອງໜ້າຈໍ."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"ລອງກົດໃສ່ໜ້າຈໍຄ້າງໄວ້ດົນຂຶ້ນກ່ອນປ່ອຍນິ້ວ."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ກະລຸນາກວດສອບວ່າທ່ານປັດຂຶ້ນຊື່ໆ, ຈາກນັ້ນຢຸດຊົ່ວຄາວ."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"ທ່ານໄດ້ສຶກສາວິທີໃຊ້ທ່າທາງແລ້ວ. ເພື່ອປິດທ່າທາງຕ່າງໆ, ໃຫ້ເຂົ້າໄປຫາການຕັ້ງຄ່າ."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"ທ່ານໃຊ້ທ່າທາງສະຫຼັບແອັບສຳເລັດແລ້ວ"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"ທ່ານໃຊ້ທ່າທາງສະຫຼັບແອັບສຳເລັດແລ້ວ."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ປັດເພື່ອສະຫຼັບແອັບ"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ເພື່ອສະຫຼັບລະຫວ່າງແອັບ, ໃຫ້ປັດຂຶ້ນຈາກລຸ່ມສຸດຂອງໜ້າຈໍທ່ານ, ກົດຄ້າງໄວ້, ຈາກນັ້ນປ່ອຍ."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"ເພື່ອສະຫຼັບລະຫວ່າງແອັບ, ໃຫ້ປັດຂຶ້ນດ້ວຍ 2 ນິ້ວຈາກລຸ່ມສຸດຂອງໜ້າຈໍທ່ານ, ກົດຄ້າງໄວ້ແລ້ວປ່ອຍ."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ສະຫຼັບແອັບ"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"ປັດຂຶ້ນຈາກລຸ່ມສຸດຂອງໜ້າຈໍຂອງທ່ານຄ້າງໄວ້ແລ້ວປ່ອຍ"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"ດີຫຼາຍ!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"ທຸກຢ່າງພ້ອມແລ້ວ"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"ແລ້ວໆ"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ການຕັ້ງຄ່າ"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ແຖບໜ້າວຽກທີ່ສະແດງຢູ່"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ແຖບໜ້າວຽກທີ່ເຊື່ອງໄວ້ຢູ່"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ແຖບການນຳທາງ"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"ສະແດງແຖບໜ້າວຽກສະເໝີ"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"ປ່ຽນໂໝດການນຳທາງ"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ເສັ້ນແບ່ງແຖບໜ້າວຽກ"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ຍ້າຍໄປຊ້າຍ/ເທິງ"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ຍ້າຍໄປຂວາ/ລຸ່ມ"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{ສະແດງອີກ # ແອັບ.}other{ສະແດງອີກ # ແອັບ.}}"</string> diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml index 91909e93b4..06c4f79088 100644 --- a/quickstep/res/values-lt/strings.xml +++ b/quickstep/res/values-lt/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Siūlomų programų funkcija įgalinta"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Siūlomų programų funkcija išjungta"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Numatoma programa: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Pasukite įrenginį"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Pasukite įrenginį, kad pereitumėte į naršymo gestais mokomąją medžiagą"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Turite perbraukti nuo dešiniojo ar kairiojo krašto"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Turite perbraukti nuo dešiniojo ar kairiojo krašto link ekrano vidurio ir pakelti pirštą"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Turite perbraukti nuo dešiniojo ar kairiojo krašto."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Turite perbraukti nuo dešiniojo ar kairiojo krašto link ekrano vidurio ir pakelti pirštą."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Išmokote, kaip sugrįžti perbraukiant iš dešinės. Toliau sužinosite, kaip perjungti programas."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Atlikote grįžimo atgal gestą"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Nebraukite per arti ekrano apačios"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Atlikote grįžimo atgal gestą."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Nebraukite per arti ekrano apačios."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Norėd. pak. grįžimo gesto jautr., eikite į sk. „Nustatymai“"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Norėdami grįžti, perbraukite"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Norėdami grįžti į ankstesnį ekraną, perbr. nuo kairiojo arba dešinio krašto link ekrano vidurio."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Jei norite grįžti į ankstesnį ekraną, perbraukite dviem pirštais nuo kairiojo arba dešiniojo krašto link ekrano vidurio."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Grįžti"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Perbraukite nuo kairiojo arba dešiniojo krašto link ekrano vidurio"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Turite perbraukti aukštyn nuo apatinio ekrano krašto"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Nepristabdykite prieš pakeldami pirštą"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Turite tiesiai perbraukti aukštyn"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Atlikote perėjimo į pagrindinį ekraną gestą. Toliau sužinosite, kaip grįžti atgal."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Atlikote perėjimo į pagrindinį ekraną gestą"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Turite perbraukti aukštyn nuo apatinio ekrano krašto."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Nepristabdykite prieš pakeldami pirštą."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Turite tiesiai perbraukti aukštyn."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Atlikote perėjimo į pagrindinį ekraną gestą. Toliau sužinosite, kaip grįžti atgal."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Atlikote perėjimo į pagrindinį ekraną gestą."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Perbraukite, kad pereitumėte į pagrindinį ekraną"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Perbraukite aukštyn nuo ekrano apačios. Atlikus šį gestą, visada nukreipiama į pagrindinį ekraną."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Perbraukite dviem pirštais nuo ekrano apačios. Šis gestas visada nukreipia į pagrindinį ekraną."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Eiti į pagrindinį ekraną"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Perbraukite aukštyn nuo ekrano apačios"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Puiku!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Turite perbraukti aukštyn nuo apatinio ekrano krašto"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Pabandykite palaikyti langą ilgiau prieš pakeldami pirštą"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Turite tiesiai perbraukti aukštyn, o tada pristabdyti"</string> - <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Išmokote naudotis gestais. Gestus galite išjungti nustatymuose."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Atlikote programų perjungimo gestą"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Eikite į pagrindinį ekraną"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Jei norite bet kada pasiekti pagrindinį ekraną, perbraukite aukštyn iš ekrano apačios"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Turite perbraukti aukštyn nuo apatinio ekrano krašto."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Pabandykite palaikyti langą ilgiau prieš pakeldami pirštą."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Turite tiesiai perbraukti aukštyn, o tada pristabdyti."</string> + <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"išmokote naudotis gestais. Gestus galite išjungti nustatymuose."</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Atlikote programų perjungimo gestą."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Perbraukite, kad perjungtumėte programas"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Norėdami perjungti programas, perbraukite aukštyn nuo ekrano apačios, palaikykite ir paleiskite."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Perjunkite programas perbraukę dviem pirštais aukštyn nuo ekrano apačios, palaikę ir paleidę."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Perjungti programas"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Perbraukite aukštyn nuo ekrano apačios, palaikykite ir paleiskite"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Puiku!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Viskas nustatyta"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Atlikta"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Nustatymai"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Užduočių juosta rodoma"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Užduočių juosta paslėpta"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Naršymo juosta"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Visada rodyti užduočių juostą"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Keisti naršymo režimą"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Užduočių juostos daliklis"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Perkelti aukštyn, kairėn"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Perkelti žemyn, dešinėn"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Rodyti dar # programą.}one{Rodyti dar # programą.}few{Rodyti dar # programas.}many{Rodyti dar # programos.}other{Rodyti dar # programų.}}"</string> diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml index c3cd8472e0..9ab8259c15 100644 --- a/quickstep/res/values-lv/strings.xml +++ b/quickstep/res/values-lv/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Ieteicamās lietotnes ir iespējotas"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Ieteicamās lietotnes ir atspējotas"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Prognozētā lietotne: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Pagrieziet ierīci"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Lūdzu, pagrieziet savu ierīci, lai pabeigtu žestu navigācijas apmācību."</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Jāvelk no pašas labās vai kreisās malas."</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Jāvelk no ekrāna labās vai kreisās malas uz vidu un jāatlaiž."</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Velciet no pašas labās vai kreisās malas."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Velciet no ekrāna labās vai kreisās malas uz vidu un atlaidiet."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Jūs esat apguvis, kā vilkt no labās malas, lai pārietu atpakaļ. Tagad mācieties pārslēgt lietotnes."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Jūs sekmīgi veicāt atgriešanās žestu."</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Nevelciet pārāk tuvu ekrāna apakšdaļai."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Jūs sekmīgi veicāt atgriešanās žestu."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Nevelciet pārāk tuvu ekrāna apakšdaļai."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Atgriešanās žesta jutīguma līmeni varat mainīt iestatījumos."</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Vilkšana, lai atgrieztos"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Lai atgrieztos iepriekšējā ekrānā, velciet no kreisās vai labās malas uz ekrāna vidu."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Lai pārietu uz iepriekšējo ekrānu, ar diviem pirkstiem velciet no kreisās vai labās malas uz ekrāna vidu."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Atpakaļ"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Velciet no ekrāna kreisās vai labās malas uz vidu."</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Jāvelk augšup no ekrāna apakšmalas."</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Pirms atlaišanas nepārtrauciet kustību."</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Jāvelk tieši uz augšu."</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Jūs sekmīgi veicāt sākuma ekrāna atvēršanas žestu. Tagad varat iemācīties, kā pāriet atpakaļ."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Jūs sekmīgi veicāt sākuma ekrāna atvēršanas žestu."</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Jāvelk augšup no ekrāna apakšmalas."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pirms atlaišanas nepārtrauciet kustību."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Jāvelk tieši uz augšu."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Jūs sekmīgi veicāt sākuma ekrāna atvēršanas žestu. Tagad varat iemācīties, kā pāriet atpakaļ."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Jūs sekmīgi veicāt sākuma ekrāna atvēršanas žestu."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Vilkšana, lai pārietu uz sākumu"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Velciet augšup no ekrāna apakšdaļas. Ar šo žestu vienmēr varat atvērt sākuma ekrānu."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Ar 2 pirkstiem velciet augšup no ekrāna apakšdaļas. Ar šo žestu vienmēr varat atvērt sākuma ekrānu."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Došanās uz sākuma ekrānu"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Velciet augšup no ekrāna apakšdaļas."</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Lieliski!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Jāvelk augšup no ekrāna apakšmalas."</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Mēģiniet ilgāk turēt logu, pirms atlaižat."</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Jāvelk tieši uz augšu un pēc tam jāaptur kustība."</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Jebkurā laikā varat doties uz sākuma ekrānu, no ekrāna apakšdaļas velkot augšup."</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Jāvelk augšup no ekrāna apakšmalas."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Mēģiniet ilgāk turēt logu, pirms atlaižat."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Jāvelk tieši uz augšu un pēc tam jāaptur kustība."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Jūs esat apguvis žestu izmantošanu. Lai izslēgtu žestus, pārejiet uz sadaļu Iestatījumi."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Jūs sekmīgi veicāt lietotņu pārslēgšanas žestu."</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Jūs sekmīgi veicāt lietotņu pārslēgšanas žestu."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Vilkšana, lai pārslēgtu lietotnes"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Lai pārslēgtu lietotnes, velciet augšup no ekrāna apakšdaļas, turiet un pēc tam atlaidiet."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Lai pārslēgtu lietotnes, ar 2 pirkstiem velciet no ekrāna apakšdaļas, turiet un pēc tam atlaidiet."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Lietotņu pārslēgšana"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Velciet augšup no ekrāna apakšdaļas, turiet un pēc tam atlaidiet."</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Ļoti labi!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Gatavs"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Gatavs"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Iestatījumi"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Uzdevumu josla tiek rādīta"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Uzdevumu josla paslēpta"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigācijas josla"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Vienmēr rādīt uzdevumu joslu"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Mainīt navigācijas režīmu"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Uzdevumu joslas atdalītājs"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Pārvietot uz augšējo/kreiso stūri"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pārvietot uz apakšējo/labo stūri"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Rādīt vēl # lietotni}zero{Rādīt vēl # lietotnes}one{Rādīt vēl # lietotni}other{Rādīt vēl # lietotnes}}"</string> diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml index 91223106a5..bd6b12a722 100644 --- a/quickstep/res/values-mk/strings.xml +++ b/quickstep/res/values-mk/strings.xml @@ -44,49 +44,43 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Предлозите за апликации се овозможени"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Предлозите за апликации се оневозможени"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Предвидена апликација: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Ротирајте го уредот"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Ротирајте го уредот за да го завршите упатството за навигација со движење"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Повлечете од крајниот десен или крајниот лев раб"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Повлечете од десниот или левиот раб кон средината на екранот и пуштете"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Повлечете од крајниот десен или крајниот лев раб."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Повлечете од десниот или левиот раб кон средината на екранот и пуштете."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Научивте како да повлекувате оддесно за враќање назад. Научете и како да се префрлате помеѓу апликациите."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Завршивте со упатството за враќање назад"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Не повлекувајте преблиску до дното на екранот"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Завршивте со упатството за враќање назад."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Не повлекувајте преблиску до долниот раб на екранот."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"За да ја промените чувствителноста, одете во „Поставки“"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Повлечете за да се вратите назад"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"За да се вратите на последниот екран, повлечете од левиот или десниот раб кон средината на екранот."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"За да се вратите на последниот екран на кој бевте, повлечете со два прста од левиот или десниот раб кон средината на екранот."</string> - <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Враќање назад"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Повлечете од левиот или десниот раб кон средината на екранот"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Повлечете нагоре од долниот раб на екранот"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Не правете пауза пред да пуштите"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Повлечете право нагоре"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Го научивте движењето за отворање на почетниот екран. Научете го и движењето за враќање назад."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Го научивте движењето за враќање на почетниот екран"</string> + <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Назад"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Повлечете нагоре од долниот раб на екранот."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Не правете пауза пред да пуштите."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Повлечете право нагоре."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Го научивте движењето за отворање на почетниот екран. Научете го и движењето за враќање назад."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Го научивте движењето за отворање на почетниот екран."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Повлечете за да одите на почетниот екран"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Повлечете нагоре од долниот раб на екранот. Ова движење секогаш ќе ве одведе на почетниот екран."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Повлечете нагоре со два прста од долниот раб на екранот. Движењево секогаш ве носи на почетниот екран."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Отворање на почетниот екран"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Повлечете нагоре од дното на екранот"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Одлично сторено!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Повлечете нагоре од долниот раб на екранот"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Задржете го прозорецот подолго пред да го пуштите"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Повлечете право нагоре, а потоа застанете"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Одете на почетниот екран"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"За да одите на вашиот почетен екран во кое било време, повлечете нагоре од дното на екранот"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Повлечете нагоре од долниот раб на екранот."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Задржете го прозорецот подолго пред да го пуштите."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Повлечете право нагоре, а потоа застанете."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Научивте како се користат движењата. За да ги исклучите движењата, одете во „Поставки“."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Го научивте движењето за префрлање помеѓу апликации"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Завршивте со упатството за префрлање помеѓу апликации."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Повлечете за префрлање помеѓу апликации"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"За да смените апликација, повлечете нагоре од дното на екранот и задржете, па пуштете."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"За да се префрлате помеѓу апликации, повлечете нагоре со два прста од долниот раб на екранот, задржете и потоа отпуштете."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Променете ја апликацијата"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Повлечете нагоре од дното на екранот и задржете, па пуштете"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Браво!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Готово"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Готово"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Поставки"</string> - <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Обидете се повторно"</string> + <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Обиди се пак"</string> <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Одлично!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"Упатство <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"Готово!"</string> - <string name="allset_hint" msgid="459504134589971527">"Повлечете нагоре за да го отворите почетниот екран"</string> + <string name="allset_hint" msgid="459504134589971527">"Повлечете нагоре за да појдете на почетен екран"</string> <string name="allset_button_hint" msgid="2395219947744706291">"Допрете го копчето за почетен екран за да одите на почетниот екран"</string> <string name="allset_description_generic" msgid="5385500062202019855">"Подготвени сте да почнете да го користите вашиот <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="default_device_name" msgid="6660656727127422487">"уред"</string> @@ -104,7 +98,7 @@ <string name="accessibility_rotate_button" msgid="4771825231336502943">"Ротирајте го екранот"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Обука за лентата со задачи"</string> <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Повлечете апликација настрана за да користите 2 апликации"</string> - <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Полека повлечете нагоре за да се прикаже лентата со задачи"</string> + <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Полека повлечете нагоре за да се прикаже „Лентата со задачи“"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Добивајте предлози за апликации според вашата рутина"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Вклучете навигација со движење во „Поставки“ за автоматско сокривање на „Лентата со задачи“"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Правете повеќе со една лента со задачи"</string> @@ -119,11 +113,8 @@ <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Брзи поставки"</string> <string name="taskbar_a11y_title" msgid="6432169809852243110">"Лента со задачи"</string> <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Лентата со задачи е прикажана"</string> - <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Лентата со задачи е скриена"</string> + <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Лентата со задачи е сокриена"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Лента за навигација"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Секогаш прикажувај „Лента“"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Променете режим на навигација"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Разделник на „Лента со задачи“"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Премести горе лево"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Премести долу десно"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Прикажи уште # апликација.}one{Прикажи уште # апликација.}other{Прикажи уште # апликации.}}"</string> diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml index b6ad6ac7a3..74fdd756d1 100644 --- a/quickstep/res/values-ml/strings.xml +++ b/quickstep/res/values-ml/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ആപ്പ് നിർദ്ദേശങ്ങൾ പ്രവർത്തനക്ഷമമാക്കി"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ആപ്പ് നിർദ്ദേശങ്ങൾ പ്രവർത്തനരഹിതമാക്കി"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"പ്രവചിച്ച ആപ്പ്: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"നിങ്ങളുടെ ഉപകരണം റൊട്ടേറ്റ് ചെയ്യുക"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ജെസ്ച്ചർ നാവിഗേഷൻ ട്യൂട്ടോറിയൽ പൂർത്തിയാക്കാൻ നിങ്ങളുടെ ഉപകരണം റൊട്ടേറ്റ് ചെയ്യുക"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"വലത്തേയറ്റത്തെയോ ഇടത്തേയറ്റത്തെയോ അരികിൽ നിന്നാണ് സ്വെെപ്പ് ചെയ്യുന്നതെന്ന് ഉറപ്പാക്കുക"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"വലതോ ഇടതോ അരികിൽ നിന്ന് സ്ക്രീനിന്റെ മധ്യഭാഗത്തേക്കാണ് സ്വെെപ്പ് ചെയ്യുന്നതെന്ന് ഉറപ്പാക്കുക"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"വലത്തേയറ്റത്തെയോ ഇടത്തേയറ്റത്തെയോ അരികിൽ നിന്ന് സ്വെെപ്പ് ചെയ്യുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"വലതോ ഇടതോ അരികിൽ നിന്ന് സ്ക്രീനിന്റെ മധ്യഭാഗത്തേക്ക് സ്വെെപ്പ് ചെയ്ത് വിടുക."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"മടങ്ങാൻ വലതുഭാഗത്ത് നിന്ന് സ്വൈപ്പ് ചെയ്യുന്ന രീതി മനസ്സിലായി. ഇനി, ആപ്പുകൾ മാറുന്ന രീതി അറിയുക."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"മടങ്ങുക ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"സ്ക്രീനിന്റെ ഏറ്റവും അടിഭാഗത്തേക്ക് സ്വെെപ്പ് ചെയ്യുന്നില്ലെന്ന് ഉറപ്പാക്കുക"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"മടങ്ങുക ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"സ്ക്രീനിന്റെ ഏറ്റവും അടിഭാഗത്തേക്ക് സ്വെെപ്പ് ചെയ്യുന്നില്ലെന്ന് ഉറപ്പാക്കുക."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ബാക്ക്ജെസ്റ്ററിന്റെ സെൻസിറ്റിവിറ്റി മാറ്റാൻ ക്രമീകരണത്തിൽ പോകൂ"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"മടങ്ങാൻ സ്വെെപ്പ് ചെയ്യുക"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"മുൻ സ്ക്രീനിലേക്ക് മടങ്ങാൻ, ഇടതോ വലതോ അരികിൽ നിന്ന് സ്ക്രീനിന്റെ നടുവിലേക്ക് സ്വെെപ്പ് ചെയ്യുക."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"മുമ്പത്തെ സ്ക്രീനിലേക്ക് മടങ്ങാൻ, 2 വിരലുകൾ ഉപയോഗിച്ച് ഇടത് അല്ലെങ്കിൽ വലത് മൂലയിൽ നിന്ന് സ്ക്രീനിന്റെ മധ്യഭാഗത്തേക്ക് സ്വൈപ്പ് ചെയ്യുക."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"മടങ്ങുക"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"ഇടതോ വലതോ അരികിൽ നിന്ന് സ്ക്രീനിന്റെ മധ്യഭാഗത്തേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"സ്ക്രീനിന്റെ താഴത്തെ അരികിൽ നിന്ന് മുകളിലേക്കാണ് സ്വെെപ്പ് ചെയ്യുന്നതെന്ന് ഉറപ്പാക്കുക"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"വിടുന്നതിന് മുമ്പ് നിങ്ങൾ താൽക്കാലികമായി നിർത്തുന്നില്ലെന്ന് ഉറപ്പാക്കുക"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"നേരെ മുകളിലേക്കാണ് സ്വെെപ്പ് ചെയ്യുന്നതെന്ന് ഉറപ്പാക്കുക"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"ഹോം ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി. അടുത്തത്, തിരികെ എങ്ങനെ പോകാമെന്ന് മനസ്സിലാക്കുക."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"ഹോം ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"സ്ക്രീനിന്റെ താഴത്തെ അരികിൽ നിന്ന് മുകളിലേക്ക് സ്വെെപ്പ് ചെയ്യുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"വിടുന്നതിന് മുമ്പ് നിങ്ങൾ താൽക്കാലികമായി നിർത്തുന്നില്ലെന്ന് ഉറപ്പാക്കുക."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"നേരെ മുകളിലേക്ക് സ്വെെപ്പ് ചെയ്യുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"ഹോമിലേക്ക് പോകുക ജെസ്ച്ചർ പൂർത്തിയാക്കി. അടുത്തത്, എങ്ങനെ മടങ്ങാമെന്ന് അറിയുക."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"ഹോമിലേക്ക് പോകുക ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"ഹോമിലേക്ക് പോകാൻ സ്വെെപ്പ് ചെയ്യുക"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"സ്ക്രീനിന്റെ താഴെ നിന്ന് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യൂ. ഈ ജെസ്ച്ചർ എപ്പോഴും ഹോം സ്ക്രീനിലേക്ക് നയിക്കുന്നു."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"സ്ക്രീനിന്റെ താഴെ നിന്ന് മുകളിലേക്ക് 2 വിരലുകൾ കൊണ്ട് സ്വൈപ്പ് ചെയ്യൂ. ഈ ജെസ്ച്ചർ എല്ലായ്പ്പോഴും ഹോം സ്ക്രീനിലേക്ക് നയിക്കുന്നു."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ഹോമിലേക്ക് പോകൂ"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"സ്ക്രീനിന്റെ താഴെ നിന്ന് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"കൊള്ളാം!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"സ്ക്രീനിന്റെ താഴത്തെ അരികിൽ നിന്ന് മുകളിലേക്കാണ് സ്വെെപ്പ് ചെയ്യുന്നതെന്ന് ഉറപ്പാക്കുക"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"റിലീസ് ചെയ്യുന്നതിന് മുമ്പ് വിൻഡോ കൂടുതൽ സമയം ഹോൾഡ് ചെയ്യാൻ ശ്രമിക്കുക"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"നേരെ മുകളിലേക്ക് സ്വെെപ്പ് ചെയ്ത് അൽപ്പസമയം പിടിച്ചുനിർത്തുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"ഏതുസമയത്തും ഹോം സ്ക്രീനിലേക്ക് പോകാൻ, സ്ക്രീനിന്റെ താഴെ നിന്ന് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യൂ"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"സ്ക്രീനിന്റെ താഴത്തെ അരികിൽ നിന്ന് മുകളിലേക്ക് സ്വെെപ്പ് ചെയ്യുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"റിലീസ് ചെയ്യുന്നതിന് മുമ്പ് വിൻഡോ കൂടുതൽ സമയം ഹോൾഡ് ചെയ്യാൻ ശ്രമിക്കുക."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"നേരെ മുകളിലേക്ക് സ്വെെപ്പ് ചെയ്ത് അൽപ്പസമയം പിടിച്ചുനിർത്തുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"ജെസ്ച്ചറുകൾ ഉപയോഗിക്കുന്ന രീതി നിങ്ങൾ മനസ്സിലാക്കി. ജെസ്ച്ചറുകൾ ഓഫാക്കാൻ ക്രമീകരണത്തിലേക്ക് പോകുക."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"ആപ്പുകൾ തമ്മിൽ മാറുക ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"ആപ്പുകൾ തമ്മിൽ മാറുക ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ആപ്പുകൾ മാറാൻ സ്വെെപ്പ് ചെയ്യുക"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ആപ്പുകൾക്കിടയിൽ മാറാൻ സ്ക്രീനിന്റെ താഴെ നിന്ന് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്ത് പിടിച്ച ശേഷം വിടുക."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"ആപ്പുകൾക്കിടയിൽ മാറാൻ സ്ക്രീനിന്റെ താഴെ നിന്ന് മുകളിലേക്ക് 2 വിരലുകൾ കൊണ്ട് സ്വൈപ്പ് ചെയ്ത് പിടിച്ച ശേഷം വിടുക."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ആപ്പുകൾ മാറുക"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"നിങ്ങളുടെ സ്ക്രീനിന്റെ താഴെ നിന്ന് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്ത് പിടിച്ച ശേഷം, വിടുക"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"അഭിനന്ദനങ്ങൾ!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"എല്ലാം സജ്ജീകരിച്ചു"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"പൂർത്തിയായി"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ക്രമീകരണം"</string> @@ -94,7 +88,7 @@ <string name="action_share" msgid="2648470652637092375">"പങ്കിടുക"</string> <string name="action_screenshot" msgid="8171125848358142917">"സ്ക്രീൻഷോട്ട്"</string> <string name="action_split" msgid="2098009717623550676">"വിഭജിക്കുക"</string> - <string name="toast_split_select_app" msgid="8464310533320556058">"സ്പ്ലിറ്റ് സ്ക്രീനിന് മറ്റൊരു ആപ്പിൽ ടാപ്പ് ചെയ്യൂ"</string> + <string name="toast_split_select_app" msgid="8464310533320556058">"സ്പ്ലിറ്റ് സ്ക്രീനിനായി മറ്റൊരു ആപ്പ് ടാപ്പുചെയ്യൂ"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"സ്ക്രീൻ വിഭജന മോഡിന് മറ്റൊരു ആപ്പ് തിരഞ്ഞെടുക്കൂ"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ഈ നടപടി എടുക്കുന്നത് ആപ്പോ നിങ്ങളുടെ സ്ഥാപനമോ അനുവദിക്കുന്നില്ല"</string> <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"നാവിഗേഷൻ ട്യൂട്ടോറിയൽ ഒഴിവാക്കണോ?"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ടാസ്ക്ബാർ കാണിച്ചിരിക്കുന്നു"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ടാസ്ക്ബാർ മറച്ചിരിക്കുന്നു"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"നാവിഗേഷൻ ബാർ"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"ടാസ്ക്ബാർ എപ്പോഴും കാണിക്കൂ"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"നാവിഗേഷൻ മോഡ് മാറ്റുക"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ടാസ്ക്ബാർ ഡിവൈഡർ"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"മുകളിലേക്കോ ഇടത്തേക്കോ നീക്കുക"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"താഴേക്കോ വലത്തേക്കോ നീക്കുക"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# ആപ്പ് കൂടി കാണിക്കുക.}other{# ആപ്പുകൾ കൂടി കാണിക്കുക.}}"</string> diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml index 8f82ebddd9..7c6314e7ab 100644 --- a/quickstep/res/values-mn/strings.xml +++ b/quickstep/res/values-mn/strings.xml @@ -23,7 +23,7 @@ <string name="recent_task_option_freeform" msgid="48863056265284071">"Чөлөөтэй хувьсах"</string> <string name="recents_empty_message" msgid="7040467240571714191">"Сүүлийн үеийн зүйл алга"</string> <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Апп ашиглалтын тохиргоо"</string> - <string name="recents_clear_all" msgid="5328176793634888831">"Бүгдийг арилгах"</string> + <string name="recents_clear_all" msgid="5328176793634888831">"Бүгдийг устгах"</string> <string name="accessibility_recent_apps" msgid="4058661986695117371">"Саяхны аппууд"</string> <string name="task_view_closed" msgid="9170038230110856166">"Ажлыг хаасан"</string> <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string> @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Санал болгож буй аппуудыг идэвхжүүлсэн"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Санал болгож буй аппуудыг идэвхгүй болгосон"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Таамаглаж буй апп: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Төхөөрөмжөө эргүүлэх"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Зангааны навигацын практик хичээлийг дуусгахын тулд төхөөрөмжөө эргүүлнэ үү"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Та баруун зах эсвэл зүүн захын ирмэгээс шударна уу"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Та баруун эсвэл зүүн ирмэгээс дэлгэцийн дунд хэсэг хүртэл шударч, суллана уу"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Та баруун зах эсвэл зүүн захын булангаас шударна уу."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Та баруун эсвэл зүүн булангаас дэлгэцийн дунд хэсэг хүртэл шударч, суллана уу."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Та буцахын тулд баруунаас хэрхэн шудрахыг мэдэж авлаа. Дараа нь аппууд хооронд хэрхэн сэлгэхийг мэдэж аваарай."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Та буцах зангааг гүйцэтгэлээ"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Та дэлгэцийн доод хэсэгтэй хэт ойр бүү шудраарай"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Та буцах зангааг гүйцэтгэлээ."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Та дэлгэцийн доод хэсэгтэй хэт ойр бүү шудраарай."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Буцах зангааны мэдрэгшлийг өөрчлөх бол Тохиргоо руу очно уу"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Буцахын тулд шудрах"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Сүүлийн дэлгэц рүү буцахын тулд дэлгэцийн зүүн эсвэл баруун булангаас дунд хэсэг рүү шударна уу."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Сүүлийн дэлгэц рүү буцахын тулд 2 хуруугаараа дэлгэцийн голоос зүүн эсвэл баруун ирмэг рүү шударна уу."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Буцах"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Зүүн эсвэл баруун ирмэгээс дэлгэцийн дунд хэсэг хүртэл шударна уу"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Та дэлгэцийн доод ирмэгээс дээш шударна уу"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Та суллахаасаа өмнө түр зогсоож болохгүй"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Та дээш чигээрээ шударна уу"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Та үндсэн нүүр лүү очих зангааг гүйцэтгэлээ. Дараа нь хэрхэн буцахыг мэдэж авна уу."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Та үндсэн нүүр лүү очих зангааг гүйцэтгэлээ."</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Та дэлгэцийн доод булангаас дээш шударна уу."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Та суллахаасаа өмнө түр зогсоож болохгүй."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Та чигээрээ шударна уу."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Та Нүүр лүү очих зангааг гүйцэтгэлээ. Дараа нь хэрхэн буцахыг мэдэж авна уу."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Та Нүүр лүү очих зангааг гүйцэтгэлээ."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Нүүр лүү очихын тулд шудрах"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Дэлгэцийнхээ доороос дээш шударна уу. Энэ зангаа таныг тогтмол Үндсэн нүүрэнд аваачна."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"2 хуруугаараа дэлгэцийн доороос дээш шударна уу. Энэ зангаа таныг тогтмол Үндсэн нүүрэнд аваачна."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Үндсэн нүүр лүү очих"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Дэлгэцийнхээ доороос дээш шударна уу"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Үнэхээр сайн ажиллалаа!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Та дэлгэцийн доод ирмэгээс дээш шударна уу"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Суллахаасаа өмнө цонхыг илүү удаан дарж үзнэ үү"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Та дээш чигээрээ шударч, дараа нь түр зогсооно уу"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Үндсэн нүүр лүүгээ хүссэн үедээ очихын тулд дэлгэцийн доороос дээш шударна уу"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Та дэлгэцийн доод булангаас дээш шударна уу."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Суллахаасаа өмнө цонхыг илүү удаан дарж үзнэ үү."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Та чигээрээ шударч, дараа нь түр зогсооно уу."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Та зангааг хэрхэн ашиглахыг мэдэж авлаа. Зангааг унтраахын тулд Тохиргоо руу очно уу."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Та аппуудыг сэлгэх зангааг гүйцэтгэлээ"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Та аппуудыг сэлгэх зангааг гүйцэтгэлээ."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Аппуудыг сэлгэхийн тулд шудрах"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Аппуудын хооронд сэлгэхийн тулд дэлгэцийнхээ доод хэсгээс дээш шударч, удаан дараад, суллана уу."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Аппуудын хооронд сэлгэхийн тулд 2 хуруугаараа дэлгэцийнхээ доод хэсгээс дээш шударч, удаан дараад, суллана уу."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Аппуудыг сэлгэх"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Дэлгэцийнхээ доод хэсгээс дээш шударч, удаан дараад суллана уу"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Сайн байна!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Бүгдийг тохируулсан"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Дууссан"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Тохиргоо"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Ажлын хэсгийг харуулсан"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Ажлын хэсгийг нуусан"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Навигацын самбар"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Ажлын хэсгийг үргэлж харуулах"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Навигацын горимыг өөрчлөх"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Ажлын хэсгийг хуваагч"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Зүүн дээд хэсэг рүү зөөх"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Баруун доод хэсэг рүү зөөх"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Өөр # аппыг харуулна уу.}other{Өөр # аппыг харуулна уу.}}"</string> diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml index 73260b2ac6..cfaf2d8864 100644 --- a/quickstep/res/values-mr/strings.xml +++ b/quickstep/res/values-mr/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"अॅप सूचना सुरू केल्या आहेत"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"अॅप सूचना बंद केल्या आहेत"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"पूर्वानुमान केलेले अॅप: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"तुमचे डिव्हाइस फिरवा"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"कृपया जेश्चर नेव्हिगेशन ट्यूटोरियल पूर्ण करण्यासाठी तुमचे डिव्हाइस फिरवा"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"तुम्ही स्क्रीनच्या अगदी उजव्या किंवा अगदी डाव्या कडेपासून स्वाइप करत आहात खात्री करा"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"तुम्ही स्क्रीनच्या उजव्या किंवा डाव्या कडेपासून मध्यभागी स्वाइप करून सोडून देत आहात याची खात्री करा"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"तुम्ही स्क्रीनच्या अगदी उजव्या किंवा अगदी डाव्या कडेला स्वाइप केल्याची खात्री करा."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"तुम्ही स्क्रीनच्या उजव्या किंवा डाव्या कडेपासून मध्यभागी स्वाइप करून सोडून दिल्याची खात्री करा."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"मागे जाण्यासाठी उजवीकडून कसे स्वाइप करावे ते शिकलात. आता पुढे, ॲप्स कशी स्विच करायची ते जाणून घ्या."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"तुम्ही गो बॅक जेश्चर पूर्ण केले आहे"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"तुम्ही स्क्रीनच्या तळाच्या अगदी जवळून स्वाइप करत नाही याची खात्री करा"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"तुम्ही गो बॅक जेश्चर पूर्ण केले."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"तुम्ही स्क्रीनच्या तळाच्या अगदी जवळून स्वाइप करत नाही याची खात्री करा."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"बॅक जेश्चरची संवेदनशीलता बदलण्यासाठी, सेटिंग्ज वर जा"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"परत जाण्यासाठी स्वाइप करा"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"मागील स्क्रीनवर परत जाण्यासाठी, स्क्रीनच्या डाव्या किंवा उजव्या कडेपासून मध्यावर स्वाइप करा."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"मागील स्क्रीनवर परत जाण्यासाठी, दोन बोटांनी डाव्या किंवा उजव्या कडेपासून स्क्रीनच्या मध्यभागी स्वाइप करा."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"मागे जा"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"डाव्या किंवा उजव्या कडेपासून स्क्रीनच्या मध्यभागी स्वाइप करा"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"तुम्ही स्क्रीनच्या तळाच्या कडेपासून वर स्वाइप करत आहात याची खात्री करा"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"तुम्ही स्क्रीनवरून बोट उचलण्यापूर्वी थांबत नाही याची खात्री करा"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"तुम्ही सरळ वर स्वाइप करत आहात याची खात्री करा"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"तुम्ही गो होम जेश्चर पूर्ण केले आहे. आता, मागे कसे जायचे ते जाणून घ्या."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"तुम्ही गो होम जेश्चर पूर्ण केले आहे"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"तुम्ही स्क्रीनच्या तळाच्या कडेपासून वर स्वाइप करत आहात याची खात्री करा."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"तुम्ही स्क्रीनवरून बोट उचलण्यापूर्वी ते थांबवत नाही याची खात्री करा."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"तुम्ही सरळ वर स्वाइप करत आहात याची खात्री करा."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"तुम्ही गो होम जेश्चर पूर्ण केले. आता, मागे कसे जायचे ते जाणून घ्या."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"तुम्ही गो होम जेश्चर पूर्ण केले."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"होमवर जाण्यासाठी स्वाइप करा"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"तुमच्या स्क्रीनच्या तळाकडून वर स्वाइप करा. हे जेश्चर तुम्हाला नेहमी होम स्क्रीनवर घेऊन जाते."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"स्क्रीनच्या तळापासून दोन बोटांनी वर स्वाइप करा. हे जेश्चर तुम्हाला नेहमी होम स्क्रीनवर घेऊन जाते."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"होमवर जा"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"तुमच्या स्क्रीनच्या तळापासून वर स्वाइप करा"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"उत्तम कामगिरी!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"तुम्ही स्क्रीनच्या तळाच्या कडेपासून वर स्वाइप करत आहात याची खात्री करा"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"विंडोवरून बोट उचलण्यापूर्वी थोडा वेळ ते तेथेच धरून ठेवा"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"तुम्ही सरळ वर स्वाइप करून थांबत आहात याची खात्री करा"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"कधीही तुमच्या होम स्क्रीनवर जाण्यासाठी, तुमच्या स्क्रीनच्या तळापासून वर स्वाइप करा"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"तुम्ही स्क्रीनच्या तळाच्या कडेपासून वर स्वाइप करत आहात याची खात्री करा."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"विंडोवरून बोट उचलण्यापूर्वी थोडा वेळ ते तेथेच धरून ठेवा."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"तुम्ही सरळ वर स्वाइप करून, त्यानंतर बोट थांबवत आहात याची खात्री करा."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"तुम्ही जेश्चर कसे वापरायचे हे शिकलात. जेश्चर बंद करण्यासाठी, सेटिंग्ज वर जा."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"तुम्ही स्विच ॲप्स जेश्चर पूर्ण केले आहे"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"तुम्ही ॲप्स स्विच करण्याचे जेश्चर पूर्ण केले."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"अॅप्स स्विच करण्यासाठी स्वाइप करा"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ॲप्सदरम्यान स्विच करण्यासाठी, स्क्रीनच्या तळापासून वर स्वाइप करा, धरून ठेवा, त्यानंतर सोडून द्या."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"ॲप्सदरम्यान स्विच करण्यासाठी, स्क्रीनच्या तळापासून दोन बोटांनी वर स्वाइप करा, धरून ठेवा नंतर सोडा."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"अॅप्स स्विच करा"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"तुमच्या स्क्रीनच्या तळापासून वर स्वाइप करा, धरून ठेवा, त्यानंतर सोडा"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"छान!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"सर्व तयार आहे"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"पूर्ण झाले"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"सेटिंग्ज"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"टास्कबार दाखवलेला आहे"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"टास्कबार लपवलेले आहे"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"नेव्हिगेशन बार"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"नेहमी टास्कबार दाखवा"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"नेव्हिगेशन मोड बदला"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"टास्कबार विभाजक"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"सर्वात वरती/डावीकडे हलवा"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"तळाशी/उजवीकडे हलवा"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{आणखी # अॅप दाखवा.}other{आणखी # अॅप्स दाखवा.}}"</string> diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml index f9b0225992..4ed6493b84 100644 --- a/quickstep/res/values-ms/strings.xml +++ b/quickstep/res/values-ms/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Cadangan apl didayakan"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Cadangan apl dilumpuhkan"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Apl yang diramalkan: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Putarkan peranti anda"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Sila putarkan peranti anda untuk melengkapkan tutorial navigasi gerak isyarat"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Pastikan anda meleret dari hujung sebelah kanan atau hujung sebelah kiri"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Pastikan anda meleret dari tepi sebelah kanan atau kiri ke bahagian tengah skrin dan lepaskan"</string> - <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Anda sudah belajar cara meleret dari kanan untuk kembali. Seterusnya, ketahui cara menukar apl."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Anda telah melengkapkan gerak isyarat undur"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Pastikan anda tidak meleret terlalu dekat dengan bahagian bawah skrin"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Pastikan anda meleret dari hujung sebelah kanan atau sebelah kiri."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Pastikan anda meleret dari tepi sebelah kanan atau kiri ke tengah skrin dan lepaskan."</string> + <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Anda belajar cara meleret dari kanan untuk kembali. Seterusnya, ketahui cara menukar apl."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Anda telah melengkapkan gerak isyarat undur."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Pastikan anda tidak meleret terlalu dekat dengan bahagian bawah skrin."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Utk mengubah kepekaan gerak isyarat undur, pergi ke Tetapan"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Leret untuk kembali"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Untuk kembali ke skrin terakhir, leret dari tepi sebelah kiri atau kanan ke tengah skrin."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Untuk kembali ke skrin terakhir, leret dengan 2 jari dari tepi kiri atau kanan ke tengah skrin."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Kembali"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Leret dari sisi kiri atau kanan ke bahagian tengah skrin"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Pastikan anda meleret ke atas dari sisi bahagian bawah skrin"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Pastikan anda tidak menjeda sebelum melepaskan leretan tersebut"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Pastikan anda meleret terus ke atas"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Anda telah melengkapkan gerak isyarat akses laman utama. Seterusnya, ketahui cara kembali."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Anda telah melengkapkan gerak isyarat akses laman utama"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Pastikan anda meleret ke atas dari tepi sebelah bawah skrin."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pastikan anda tidak menjeda sebelum melepaskan gerak isyarat tersebut."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Pastikan anda meleret terus ke atas."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Anda telah melengkapkan gerak isyarat pergi ke Laman Utama. Seterusnya, ketahui cara kembali."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Anda telah melengkapkan gerak isyarat pergi ke Laman Utama."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Leret untuk kembali ke laman utama"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Leret ke atas dari bahagian bawah skrin. Gerak isyarat ini sentiasa membawa anda ke Skrin utama."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Leret ke atas dengan 2 jari dari bawah skrin. Gerak isyarat ini sentiasa bawa anda ke Skrin utama."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Pergi ke skrin utama"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Leret ke atas dari bahagian bawah skrin anda"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Syabas!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Pastikan anda meleret ke atas dari sisi bahagian bawah skrin"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Cuba tahan tetingkap untuk tempoh yang lebih lama sebelum melepaskan"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Pastikan anda meleret terus ke atas, kemudian menjeda"</string> - <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Anda sudah belajar cara menggunakan gerak isyarat. Untuk mematikan gerak isyarat, pergi ke Tetapan."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Anda telah melengkapkan gerak isyarat menukar apl"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Pergi ke laman utama"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Untuk pergi ke skrin utama anda pada bila-bila masa, leret ke atas dari bahagian bawah skrin anda"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Pastikan anda meleret ke atas dari tepi sebelah bawah skrin."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Cuba tahan tetingkap untuk tempoh yang lebih lama sebelum melepaskan."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Pastikan anda meleret ke atas, kemudian menjeda."</string> + <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Anda mempelajari cara menggunakan gerak isyarat. Untuk mematikan gerak isyarat, pergi ke Tetapan."</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Anda telah melengkapkan gerak isyarat menukar apl."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Leret untuk menukar apl"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Untuk beralih antara apl, leret ke atas dari bahagian bawah skrin anda, tahan, kemudian lepaskan."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Untuk beralih antara apl, leret ke atas dengan 2 jari dari bawah skrin, tahan, kemudian lepaskan."</string> - <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Beralih antara apl"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Leret ke atas dari bahagian bawah skrin anda, tahan, kemudian lepaskan"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Syabas!"</string> + <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Tukar apl"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Selesai"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Selesai"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Tetapan"</string> @@ -86,7 +80,7 @@ <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bagus!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"Siap!"</string> - <string name="allset_hint" msgid="459504134589971527">"Leret ke atas untuk ke laman utama"</string> + <string name="allset_hint" msgid="459504134589971527">"Leret ke atas untuk mencapai laman utama"</string> <string name="allset_button_hint" msgid="2395219947744706291">"Ketik butang skrin utama untuk pergi ke skrin utama anda"</string> <string name="allset_description_generic" msgid="5385500062202019855">"Anda sudah sedia untuk mula menggunakan <xliff:g id="DEVICE">%1$s</xliff:g> anda"</string> <string name="default_device_name" msgid="6660656727127422487">"peranti"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Bar Tugas dipaparkan"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Bar Tugas disembunyikan"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Bar navigasi"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Sentiasa paparkan Bar Tugas"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Tukar mod navigasi"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Pembahagi Bar Tugas"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Alihkan ke atas/kiri"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Alihkan ke bawah/kanan"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Tunjukkan # lagi apl.}other{Tunjukkan # lagi apl.}}"</string> diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml index 9e3c739821..617473c66f 100644 --- a/quickstep/res/values-my/strings.xml +++ b/quickstep/res/values-my/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"အက်ပ်အကြံပြုချက်များ ဖွင့်ထားသည်"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"အက်ပ်အကြံပြုချက်များကို ပိတ်ထားသည်"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ကြိုတင်မှန်းဆထားသော အက်ပ်− <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"သင့်စက်ကို လှည့်ပါ"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"လက်ဟန်ဖြင့် လမ်းညွှန်ခြင်း ရှင်းလင်းပို့ချချက်အား အပြီးသတ်ရန် သင့်စက်ကို လှည့်ပါ"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ညာ (သို့) ဘယ်အစွန်း၏ ခပ်လှမ်းလှမ်းမှ ပွတ်ဆွဲကြောင်း သေချာပါစေ"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"ဖန်သားပြင်၏ ညာ (သို့) ဘယ်အစွန်းမှ အလယ်သို့ ပွတ်ဆွဲပြီး လွှတ်လိုက်ကြောင်း သေချာပါစေ"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"ညာ သို့မဟုတ် ဘယ်ဘက်အစွန်မှ ပွတ်ဆွဲကြောင်း သေချာပါစေ။"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"စခရင်၏ ညာ သို့မဟုတ် ဘက်ဘက်အစွန်မှ အလယ်သို့ ပွတ်ဆွဲပြီး လွှတ်လိုက်ကြောင်း သေချာပါစေ။"</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"နောက်ပြန်သွားရန် ညာဘက်မှပွတ်ဆွဲနည်းကို သိသွားပါပြီ။ နောက်အဆင့်တွင် အက်ပ်များပြောင်းနည်းကို လေ့လာပါ။"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"နောက်သို့ လက်ဟန် အပြီးသတ်လိုက်ပါပြီ"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"ဖန်သားပြင် အောက်ခြေနှင့် အလွန်နီးကပ်စွာ ပွတ်ဆွဲခြင်းမရှိကြောင်း သေချာပါစေ"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"နောက်ဆုတ်လက်ဟန် ရှင်းလင်းပို့ချချက် ပြီးပါပြီ။"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ဖန်သားပြင် အောက်ခြေနှင့် အလွန်နီးကပ်စွာ ပွတ်ဆွဲခြင်းမရှိကြောင်း သေချာပါစေ။"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"နောက်ဆုတ်လက်ဟန်၏ အာရုံခံစွမ်းကိုပြောင်းရန် ‘ဆက်တင်များ’ သို့ သွားပါ"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"နောက်ပြန်သွားရန် ပွတ်ဆွဲပါ"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"နောက်ဆုံးဖန်သားပြင်သို့ ပြန်သွားရန် ဘယ်ဘက် (သို့) ညာဘက်အစွန်မှ ဖန်သားပြင်အလယ်သို့ ပွတ်ဆွဲပါ။"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"ပြီးခဲ့သည့်ဖန်သားပြင်သို့သွားရန် ဘယ်ဘက် (သို့) ညာဘက်အစွန်းမှ ဖန်သားပြင်အလယ်သို့ လက် ၂ ချောင်းဖြင့် ပွတ်ဆွဲပါ။"</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"နောက်သို့"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"ဖန်သားပြင်၏ ဘယ် (သို့) ညာအစွန်းမှ အလယ်သို့ ပွတ်ဆွဲပါ"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"ဖန်သားပြင် အောက်ခြေအစွန်းမှ အပေါ်သို့ ပွတ်ဆွဲကြောင်း သေချာပါစေ"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"လက်မလွှတ်ခင် ခဏရပ်ခြင်းမရှိကြောင်း သေချာပါစေ"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"အပေါ်တည့်တည့်သို့ ပွတ်ဆွဲကြောင်း သေချာပါစေ"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"ပင်မစာမျက်နှာသို့သွားသည့် လက်ဟန် အပြီးသတ်လိုက်ပါပြီ။ နောက်အဆင့်တွင် နောက်သို့ပြန်သွားပုံကို လေ့လာပါ။"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"ပင်မစာမျက်နှာသို့သွားသည့် လက်ဟန် အပြီးသတ်လိုက်ပါပြီ"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ဖန်သားပြင် အောက်ခြေအစွန်မှ အပေါ်သို့ ပွတ်ဆွဲကြောင်း သေချာပါစေ။"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"လက်မလွှတ်ခင် ခဏရပ်ခြင်းမရှိကြောင်း သေချာပါစေ။"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"အပေါ်တည့်တည့်သို့ ပွတ်ဆွဲကြောင်း သေချာပါစေ။"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"ပင်မစာမျက်နှာသို့သွားသည့် လက်ဟန် ရှင်းလင်းပို့ချချက် ပြီးပါပြီ။ နောက်အဆင့်တွင် နောက်သို့ပြန်သွားနည်းကို လေ့လာပါ။"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"ပင်မစာမျက်နှာသို့သွားသည့် လက်ဟန် ရှင်းလင်းပို့ချချက် ပြီးပါပြီ။"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"ပင်မစာမျက်နှာသို့သွားရန် ပွတ်ဆွဲပါ"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"သင့်ဖန်သားပြင် အောက်ခြေမှ အပေါ်သို့ပွတ်ဆွဲပါ။ ဤလက်ဟန်ဖြင့် ပင်မစာမျက်နှာသို့ အမြဲပြန်သွားနိုင်သည်။"</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"ဖန်သားပြင်အောက်မှ အပေါ်သို့ လက် ၂ ချောင်းဖြင့် ပွတ်ဆွဲပါ။ ဤလက်ဟန်က ပင်မစာမျက်နှာသို့ အမြဲပို့ပေးမည်။"</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ပင်မစာမျက်နှာသွားရန်"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"သင့်ဖန်သားပြင် အောက်ခြေမှ အပေါ်သို့ ပွတ်ဆွဲပါ"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"တော်ပါပေသည်။"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"ဖန်သားပြင် အောက်ခြေအစွန်းမှ အပေါ်သို့ ပွတ်ဆွဲကြောင်း သေချာပါစေ"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"မလွှတ်ခင် ဝင်းဒိုးကို အချိန်ကြာကြာ ဖိထားကြည့်ပါ"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"အပေါ်တည့်တည့်သို့ ပွတ်ဆွဲပြီးနောက် ခဏရပ်ကြောင်း သေချာပါစေ"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ပင်မစာမျက်နှာသို့ သွားပါ"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"ပင်မစာမျက်နှာသို့ အချိန်မရွေး ပြန်သွားရန် စခရင်အောက်ခြေမှ အပေါ်သို့ ပွတ်ဆွဲပါ"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ဖန်သားပြင် အောက်ခြေအစွန်မှ အပေါ်သို့ ပွတ်ဆွဲကြောင်း သေချာပါစေ။"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"မလွှတ်ခင် ဝင်းဒိုးကို အချိန်ကြာကြာ ဖိထားကြည့်ပါ။"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"အပေါ်တည့်တည့်သို့ ပွတ်ဆွဲပြီးနောက် ခဏရပ်ကြောင်း သေချာပါစေ။"</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"လက်ဟန်များသုံးနည်းကို သင်သိသွားပါပြီ။ လက်ဟန်များကို ပိတ်ရန် ဆက်တင်များသို့ သွားပါ။"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"အက်ပ်များပြောင်းသည့် လက်ဟန် အပြီးသတ်လိုက်ပါပြီ"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"အက်ပ်များပြောင်းသည့် လက်ဟန် ရှင်းလင်းပို့ချချက် ပြီးပါပြီ။"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"အက်ပ်များပြောင်းရန် ပွတ်ဆွဲပါ"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"အက်ပ်တစ်ခုမှတစ်ခုသို့ ပြောင်းရန် စခရင်အောက်ခြေမှ အပေါ်သို့ ပွတ်ဆွဲ၍ ဖိထားပြီးနောက် လွှတ်ပါ။"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"အက်ပ်များကြားပြောင်းရန် ဖန်သားပြင်အောက်မှ အပေါ်သို့ လက် ၂ ချောင်းဖြင့်ဆွဲ၍ ထိန်းထားပြီး လွှတ်ပါ။"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"အက်ပ်များ ကူးပြောင်းရန်"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"သင့်ဖန်သားပြင် အောက်ခြေမှ အပေါ်သို့ ပွတ်ဆွဲ၍ ဖိထားပြီးလွှတ်လိုက်ပါ"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"အလွန်ကောင်းပါသည်။"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"အားလုံးအဆင်သင့်ဖြစ်ပါပြီ"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"ပြီးပြီ"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ဆက်တင်များ"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar ပြထားသည်"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar ဖျောက်ထားသည်"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"လမ်းညွှန်ဘား"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"‘လုပ်ဆောင်စရာဘား’ အမြဲပြပါ"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"ရွှေ့ကြည့်သည့်မုဒ် ပြောင်းရန်"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"လုပ်ဆောင်စရာဘား ပိုင်းခြားစနစ်"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"အပေါ်/ဘယ်ဘက်သို့ ရွှေ့ရန်"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"အောက်ခြေ/ညာဘက်သို့ ရွှေ့ရန်"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{နောက်ထပ်အက်ပ် # ခု ပြပါ။}other{နောက်ထပ်အက်ပ် # ခု ပြပါ။}}"</string> diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml index 5fe4abda57..e4d75bebcd 100644 --- a/quickstep/res/values-nb/strings.xml +++ b/quickstep/res/values-nb/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Appforslag er på"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Appforslag er slått av"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Foreslått app: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Roter enheten"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Roter enheten for å fullføre veiledningen for navigasjon med bevegelser"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Sørg for at du sveiper fra kanten helt til høyre eller venstre"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Sørg for at du sveiper fra den høyre eller venstre kanten til midten av skjermen og slipper"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Sørg for at du sveiper fra kanten helt til høyre eller venstre."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Sørg for at du sveiper fra den høyre eller venstre kanten til midten av skjermen og slipper."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Du har lært hvordan du sveiper fra høyre for å gå tilbake. I neste trinn lærer du å bytte app."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Du har fullført bevegelsen for å gå tilbake"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Sørg for at du ikke sveiper for nær bunnen av skjermen"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Du har fullført bevegelsen for å gå tilbake."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Sørg for at du ikke sveiper for nær bunnen av skjermen."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Gå til Innstillinger for å endre tilbakebevegelsefølsomheten"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Sveip for å gå tilbake"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"For å gå tilbake til forrige skjerm, sveip fra venstre eller høyre kant til midten av skjermen."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"For å gå tilbake til den forrige skjermen, sveip med to fingre fra den venstre eller høyre kanten til midten av skjermen."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Gå tilbake"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Sveip fra venstre eller høyre kant til midten av skjermen"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Sørg for at du sveiper opp fra den nederste kanten av skjermen"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Sørg for at du ikke setter på pause før du slipper"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Sørg for at du sveiper rett opp"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Du har fullført bevegelsen for å gå til startskjermen. I neste trinn ser du hvordan du går tilbake."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Du har fullført bevegelsen for å gå til startskjermen"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Sørg for at du sveiper opp fra den nederste kanten av skjermen."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Sørg for at du ikke setter på pause før du slipper."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Sørg for at du sveiper rett opp."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Du har fullført bevegelsen for å gå til startskjermen. I neste trinn ser du hvordan du går tilbake."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Du har fullført bevegelsen for å gå til startskjermen."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Sveip for å gå til startskjermen"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Sveip opp fra bunnen av skjermen. Denne bevegelsen tar deg alltid til startskjermen."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Sveip opp med to fingre fra bunnen av skjermen. Denne bevegelsen tar deg alltid til startskjermen."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Gå til startskjermen"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Sveip opp fra bunnen av skjermen"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Bra jobbet!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Sørg for at du sveiper opp fra den nederste kanten av skjermen"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Prøv å holde vinduet lenger før du slipper"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Sørg for at du sveiper rett opp, og så stopper du"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"For å gå til startskjermen, sveip opp fra bunnen av skjermen når som helst"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Sørg for at du sveiper opp fra den nederste kanten av skjermen."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Prøv å holde vinduet lenger før du slipper."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Sørg for at du sveiper rett opp, og så stopper du."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Du har lært hvordan du bruker bevegelser. For å slå av bevegelser, gå til Innstillinger."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Du har fullført bevegelsen for å bytte app"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Du har fullført bevegelsen for å bytte app."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Sveip for å bytte app"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"For å bytte mellom apper, sveip opp fra bunnen av skjermen, hold, og slipp"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"For å bytte mellom apper, sveip opp med to fingre fra bunnen av skjermen, hold, og slipp."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Bytt app"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Sveip opp fra bunnen av skjermen, hold, og slipp"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Bra!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Ferdig"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Ferdig"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Innstillinger"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Oppgavelinjen vises"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Oppgavelinjen er skjult"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigasjonsrad"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Vis alltid oppgavelinjen"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Endre navigasjonsmodus"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Skille for oppgavelinjen"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytt til øverst/venstre"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytt til nederst/høyre"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Vis # app til.}other{Vis # apper til.}}"</string> diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml index 7a86f280cd..b9e8e1cb8e 100644 --- a/quickstep/res/values-ne/strings.xml +++ b/quickstep/res/values-ne/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"सिफारिस गरिएका एप देखाउने सुविधा अन गरिएको छ"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"सिफारिस गरिएका एपहरू देखाउने सुविधा असक्षम पारिएको छ"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"पूर्वानुमान गरिएको एप: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"आफ्नो डिभाइस रोटेट गर्नुहोस्"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"इसारामार्फत गरिने नेभिगेसनको ट्युटोरियल पूरा गर्न कृपया आफ्नो डिभाइस रोटेट गर्नुहोस्"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"स्क्रिनको सबैभन्दा दायाँ किनारा वा सबैभन्दा बायाँ किनाराबाट स्वाइप गर्नुहोस्"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"स्क्रिनको दायाँ वा बायाँ किनाराबाट मध्य भागसम्म स्वाइप गर्नुहोस् अनि औँला उठाउनुहोस्"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"स्क्रिनको सबैभन्दा दायाँ किनारा वा सबैभन्दा बायाँ किनाराबाट स्वाइप गर्नुहोस्।"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"स्क्रिनको दायाँ वा बायाँ किनाराबाट मध्य भागसम्म स्वाइप गर्नुहोस् अनि औँला उठाउनुहोस्।"</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"तपाईंले स्क्रिनको दायाँ किनाराबाट स्वाइप गरेर अघिल्लो स्क्रिनमा फर्कने तरिका सिक्नुभयो। अब एउटा एपबाट अर्को एपमा जाने तरिका सिक्नुहोस्।"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"तपाईंले \"पछाडि जानुहोस्\" नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"स्क्रिनको फेदको धेरै नजिकसम्म स्वाइप नगर्नुहोस्"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"तपाईंले \'पछाडि जानुहोस्\' नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो।"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"स्क्रिनको फेदको धेरै नजिकसम्म स्वाइप नगर्नुहोस्।"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"\'पछाडि\' नामक इसाराको संवेदनशीलता बदल्न सेटिङमा जानुहोस्"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"पछाडि जान स्वाइप गर्नुहोस्"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"यसअघिको स्क्रिनमा फर्कन स्क्रिनको बायाँ वा दायाँ किनाराबाट मध्य भागसम्म स्वाइप गर्नुहोस्।"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"तपाईं यसअघि जुन स्क्रिनमा हुनुहुन्थ्यो त्यही स्क्रिनमा फर्कन चाहनुहुन्छ भने २ वटा औँला प्रयोग गरी स्क्रिनको बायाँ वा दायाँ किनाराबाट मध्य भागसम्म स्वाइप गर्नुहोस्।"</string> - <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"पछाडि जाने तरिका"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"स्क्रिनको बायाँ वा दायाँ किनाराबाट मध्य भागसम्म स्वाइप गर्नुहोस्"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"औँला उठाउनुअघि नरोकिनुहोस्"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"सीधै माथितिर स्वाइप गर्नुहोस्"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"तपाईंले \"होम स्क्रिनमा जानुहोस्\" नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो। अब पछाडि जाने तरिका सिक्नुहोस्।"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"तपाईंले \"होम स्क्रिनमा जानुहोस्\" नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो"</string> + <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"पछाडि जानुहोस्"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्।"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"औँला उठाउनुअघि नरोकिनुहोस्।"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"सीधै माथितिर स्वाइप गर्नुहोस्।"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"तपाईंले \'होम स्क्रिनमा जानुहोस्\' नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो। अब पछाडि जाने तरिका सिक्नुहोस्।"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"तपाईंले \'होम स्क्रिनमा जानुहोस्\' नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो।"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"होम स्क्रिनमा जान स्वाइप गर्नुहोस्"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्। यो इसारा प्रयोग गर्दा सधैँ होम स्क्रिन खुल्छ।"</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"२ वटा औँला प्रयोग गरी स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्। यो जेस्चर प्रयोग गर्दा सधैँ होम स्क्रिन खुल्छ।"</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"होम स्क्रिनमा जाने तरिका"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"अद्भुत!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"स्क्रिनबाट औँला उठाउनुअघि एपको विन्डोमा केही बेर छोइराख्नुहोस्"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"सीधै माथितिर स्वाइप गर्नुहोस् अनि रोकिनुहोस्"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"जुनसुकै बेला होम स्क्रिनमा जान स्क्रिनको पुछारबाट माथितिर स्वाइप गर्नुहोस्"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्।"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"स्क्रिनबाट औँला उठाउनुअघि एपको विन्डोमा केही बेर छोइराख्नुहोस्।"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"सीधै माथितिर स्वाइप गर्नुहोस् अनि रोकिनुहोस्।"</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"तपाईंले इसाराहरू प्रयोग गर्ने तरिका सिक्नुभयो। इसारा अफ गर्न सेटिङमा जानुहोस्।"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"तपाईंले \"एउटा एपबाट अर्को एपमा जानुहोस्\" नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"तपाईंले \'एउटा एपबाट अर्को एपमा जानुहोस्\' नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो।"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"एउटा एपबाट अर्को एपमा जान स्वाइप गर्नुहोस्"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"एउटा एपबाट अर्कोमा जान स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्, छोइराख्नुहोस् अनि औँला उठाउनुहोस्।"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"एउटा एपबाट अर्कोमा जान २ वटा औँला प्रयोग गरी स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्, छोइराख्नुहोस् अनि औँला उठाउनुहोस्।"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"एउटा एपबाट अर्को एपमा जाने तरिका"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्, छोइराख्नुहोस् अनि औँला उठाउनुहोस्"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"स्याबास!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"सबै तयार छ"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"सम्पन्न भयो"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"सेटिङ"</string> @@ -100,7 +94,7 @@ <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"नेभिगेसन ट्युटोरियल स्किप गर्ने हो?"</string> <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"तपाईं पछि <xliff:g id="NAME">%1$s</xliff:g> नामक एपमा गई यो ट्युटोरियल भेट्टाउन सक्नुहुन्छ"</string> <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"रद्द गर्नुहोस्"</string> - <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"स्किप गर्नुहोस्"</string> + <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"स्किप गर्नु…"</string> <string name="accessibility_rotate_button" msgid="4771825231336502943">"स्क्रिन घुमाउनुहोस्"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"टास्कबार एजुकेसन"</string> <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"एपलाई छेउतिर ड्र्याग गरेर एकै पटक २ वटा एप चलाउनुहोस्"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"टास्कबार देखाइएको छ"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"टास्कबार लुकाइएको छ"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"नेभिगेसन बार"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"टास्कबार सधैँ देखाइयोस्"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"नेभिगेसन मोड बदल्नुहोस्"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"टास्कबार डिभाइडर"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"सिरान/बायाँतिर सार्नुहोस्"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"फेद/दायाँतिर सार्नुहोस्"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{थप # एप देखाइयोस्।}other{थप # वटा एप देखाइयोस्।}}"</string> diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml index ed63bade50..f2a9d88fe0 100644 --- a/quickstep/res/values-nl/strings.xml +++ b/quickstep/res/values-nl/strings.xml @@ -44,46 +44,40 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App-suggesties staan aan"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App-suggesties staan uit"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Voorspelde app: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Het apparaat draaien"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Draai het apparaat om de tutorial voor navigatie met gebaren af te ronden"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Swipe vanaf de rechter- of linkerrand"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Swipe vanaf de rechter- of linkerrand naar het midden van het scherm en laat los"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Swipe helemaal vanaf de rechter- of linkerrand."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Swipe vanaf de rechter- of linkerrand naar het midden van het scherm en laat los."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Je weet nu hoe je vanaf rechts kunt swipen om terug te gaan. Ontdek nu hoe je tussen apps schakelt."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Je weet nu hoe je het gebaar Terug maakt"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Swipe niet te dicht bij de onderkant van het scherm"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Je weet nu hoe je het gebaar Terug maakt."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Swipe niet te dicht bij de onderkant van het scherm."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Open Instellingen om de gevoeligheid van Terug te wijzigen"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Swipe om terug te gaan"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Swipe vanaf de linker- of rechterrand naar het midden om terug te gaan naar het vorige scherm."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Als je wilt teruggaan naar het laatste scherm, swipe je met 2 vingers vanaf de linker- of rechterrand naar het midden van het scherm."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Terug"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Swipe vanaf de linker- of rechterrand naar het midden van het scherm"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Swipe vanaf de onderrand van het scherm omhoog"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Pauzeer niet voordat je loslaat"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Swipe recht omhoog"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Je weet nu hoe je het gebaar Naar startscherm maakt. Ontdek als volgende hoe je kunt teruggaan."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Je weet nu hoe je teruggaat naar het startscherm"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Swipe vanaf de onderrand van het scherm omhoog."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pauzeer niet voordat je loslaat."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Swipe recht omhoog."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Je hebt geleerd hoe je weer naar het startscherm gaat. Ontdek nu hoe je weer teruggaat."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Je weet nu hoe je teruggaat naar het startscherm."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Swipe om naar het startscherm te gaan"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swipe omhoog vanaf de onderkant van het scherm. Met dit gebaar ga je altijd naar het startscherm."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Swipe met 2 vingers omhoog vanaf de onderkant van het scherm. Met dit gebaar ga je altijd naar het startscherm."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Naar het startscherm"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Swipe omhoog vanaf de onderkant van het scherm"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Goed gedaan"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Swipe vanaf de onderrand van het scherm omhoog"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Houd je vinger langer op het venster voordat je loslaat"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Swipe recht omhoog en pauzeer dan"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Ga op elk moment naar je startscherm door omhoog te swipen vanaf de onderkant van je scherm"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Swipe vanaf de onderrand van het scherm omhoog."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Houd het venster langer vast voordat je loslaat."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Swipe recht omhoog en pauzeer dan."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Je weet nu hoe je gebaren gebruikt. Als je gebaren wilt uitzetten, kun je dat via Instellingen doen."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Je weet nu hoe je het gebaar Schakelen tussen apps maakt"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Je weet nu hoe je het gebaar Schakelen tussen apps maakt."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swipe om tussen apps te schakelen"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Swipe omhoog vanaf de onderkant van het scherm, houd vast en laat los om tussen apps te schakelen."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Swipe met 2 vingers omhoog vanaf de onderkant van het scherm, houd vast en laat los om tussen apps te schakelen."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Schakelen tussen apps"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Swipe omhoog vanaf de onderkant van het scherm, houd je vinger op het scherm en laat dan los"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Goed bezig"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Klaar"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Klaar"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Instellingen"</string> <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Probeer opnieuw"</string> - <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Dat gaat lekker"</string> + <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Dat gaat lekker."</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"Klaar"</string> <string name="allset_hint" msgid="459504134589971527">"Swipe omhoog om naar het startscherm te gaan"</string> @@ -104,7 +98,7 @@ <string name="accessibility_rotate_button" msgid="4771825231336502943">"Scherm draaien"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Taakbalk Onderwijs"</string> <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Sleep een app naar de zijkant om 2 apps tegelijk te gebruiken"</string> - <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Swipe langzaam omhoog om de taakbalk te tonen"</string> + <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Swipe kort omhoog om de taakbalk te tonen"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Krijg app-suggesties op basis van je routine"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Zet navigatie met gebaren aan bij Instellingen om de Taakbalk automatisch te verbergen"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Doe meer met de taakbalk"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taakbalk wordt getoond"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taakbalk is verborgen"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigatiebalk"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Taakbalk altijd tonen"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Navigatiemodus wijzigen"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Scheiding voor Taakbalk"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Naar boven/links verplaatsen"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Naar beneden/rechts verplaatsen"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Nog # app tonen.}other{Nog # apps tonen.}}"</string> diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml index 5e2a16eb7b..2affa6f5d9 100644 --- a/quickstep/res/values-or/strings.xml +++ b/quickstep/res/values-or/strings.xml @@ -21,7 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="recent_task_option_pin" msgid="7929860679018978258">"ପିନ୍"</string> <string name="recent_task_option_freeform" msgid="48863056265284071">"ଫ୍ରିଫର୍ମ"</string> - <string name="recents_empty_message" msgid="7040467240571714191">"ବର୍ତ୍ତମାନର କୌଣସି ଆଇଟମ ନାହିଁ"</string> + <string name="recents_empty_message" msgid="7040467240571714191">"କୌଣସି ସାମ୍ପ୍ରତିକ ଆଇଟମ୍ ନାହିଁ"</string> <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ଆପ ବ୍ୟବହାର ସେଟିଂସ"</string> <string name="recents_clear_all" msgid="5328176793634888831">"ସବୁ ଖାଲି କରନ୍ତୁ"</string> <string name="accessibility_recent_apps" msgid="4058661986695117371">"ବର୍ତ୍ତମାନର ଆପ୍"</string> @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକୁ ସକ୍ଷମ କରାଯାଇଛି"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକୁ ଅକ୍ଷମ କରାଯାଇଛି"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ପୂର୍ବାନୁମାନ କରାଯାଇଥିବା ଆପ୍: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"ଆପଣଙ୍କ ଡିଭାଇସକୁ ରୋଟେଟ କରନ୍ତୁ"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ଜେଶ୍ଚର ନାଭିଗେସନ ଟ୍ୟୁଟୋରିଆଲ ସମ୍ପୂର୍ଣ୍ଣ କରିବାକୁ ଦୟାକରି ଆପଣଙ୍କ ଡିଭାଇସ ରୋଟେଟ କରନ୍ତୁ"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ଆପଣ ସ୍କ୍ରିନର ଏକଦମ୍-ଡାହାଣ ବା ବାମ ଧାରରୁ ସ୍ୱାଇପ୍ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"ଆପଣ ସ୍କ୍ରିନର ଡାହାଣ ବା ବାମ ଧାରରୁ ମଝିକୁ ସ୍ୱାଇପ କରି ଛାଡ଼ି ଦେଉଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"ଆପଣ ସ୍କ୍ରିନର ଏକଦମ୍-ଡାହାଣ ବା ବାମ ଧାରରୁ ସ୍ୱାଇପ୍ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"ଆପଣ ସ୍କ୍ରିନର ଡାହାଣ ବା ବାମ ଧାରରୁ ମଝିକୁ ସ୍ୱାଇପ୍ କରି ଛାଡ଼ି ଦେଉଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"ଆପଣ ଡାହାଣରୁ ସ୍ୱାଇପ୍ କରି ପଛକୁ କିପରି ଫେରିବେ ତାହା ଜାଣିଲେ। ତା\'ପରେ, ଆପକୁ କିପରି ସ୍ୱିଚ୍ କରିବେ ତାହା ଜାଣନ୍ତୁ।"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"ଆପଣ \'ପଛକୁ ଫେରନ୍ତୁ\' ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"ଆପଣ ସ୍କ୍ରିନର ତଳଭାଗର ଅତି ନିକଟରୁ ସ୍ୱାଇପ କରୁନଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"ଆପଣ \'ପଛକୁ ଫେରନ୍ତୁ\' ଜେଶ୍ଚର୍ ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ଆପଣ ସ୍କ୍ରିନର ତଳଭାଗର ଅତି ନିକଟରୁ ସ୍ୱାଇପ୍ କରୁନଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ପଛକୁ ଫେରିବା ଜେଶ୍ଚରର ସମ୍ବେଦନଶୀଳତା ବଦଳାଇବାକୁ ସେଟିଂସକୁ ଯାଆନ୍ତୁ"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"ପଛକୁ ଫେରିବା ପାଇଁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string> - <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ପୂର୍ବ ସ୍କ୍ରିନକୁ ଫେରିବା ପାଇଁ, ସ୍କ୍ରିନର ବାମ କିମ୍ବା ଡାହାଣ ଧାରରୁ ମଝିକୁ ସ୍ୱାଇପ କରନ୍ତୁ।"</string> + <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ପୂର୍ବବର୍ତ୍ତୀ ସ୍କ୍ରିନକୁ ଫେରିବା ପାଇଁ, ସ୍କ୍ରିନର ବାମ କିମ୍ବା ଡାହାଣ ଧାରରୁ ମଝିକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ।"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"ପୂର୍ବ ସ୍କ୍ରିନକୁ ଫେରିବା ପାଇଁ, ସ୍କ୍ରିନର ବାମ କିମ୍ବା ଡାହାଣ ଧାରରୁ ମଝିକୁ 2ଟି ଆଙ୍ଗୁଠିରେ ସ୍ୱାଇପ କରନ୍ତୁ।"</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"ପଛକୁ ଫେରନ୍ତୁ"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"ସ୍କ୍ରିନର ବାମ କିମ୍ବା ଡାହାଣ ଧାରରୁ ମଝିକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"ଆପଣ ସ୍କ୍ରିନର ତଳ ଧାରରୁ ଉପରକୁ ସ୍ୱାଇପ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"ଆପଣ ଛାଡ଼ିବା ପୂର୍ବରୁ ବିରତ କରୁନଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"ଆପଣ ସିଧା ଉପରକୁ ସ୍ୱାଇପ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"ଆପଣ \'ହୋମକୁ ଯାଆନ୍ତୁ\' ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି। ତା\'ପରେ, ପଛକୁ କିପରି ଫେରିବେ ତାହା ଜାଣନ୍ତୁ।"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"ଆପଣ \'ମୂଳପୃଷ୍ଠାକୁ ଯାଆନ୍ତୁ\' ଜେଶ୍ଚର୍ ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ଆପଣ ସ୍କ୍ରିନର ତଳ ଧାରରୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ଆପଣ ଛାଡ଼ିବା ପୂର୍ବରୁ ବିରତ କରୁନଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ଆପଣ ସିଧା ଉପରକୁ ସ୍ୱାଇପ୍ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"ଆପଣ ହୋମ ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି। ତା\'ପରେ, ପଛକୁ କିପରି ଫେରିବେ ତାହା ଜାଣନ୍ତୁ।"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"ଆପଣ ହୋମ ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"ହୋମକୁ ଯିବା ପାଇଁ ସ୍ୱାଇପ କରନ୍ତୁ"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ଆପଣଙ୍କ ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ। ଏହି ଜେଶ୍ଚର ସର୍ବଦା ଆପଣଙ୍କୁ ହୋମ ସ୍କ୍ରିନକୁ ନେଇଥାଏ।"</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"2ଟି ଆଙ୍ଗୁଠିରେ ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ। ଏହି ଜେଶ୍ଚର ସର୍ବଦା ଆପଣଙ୍କୁ ହୋମ ସ୍କ୍ରିନକୁ ନେଇଥାଏ।"</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ହୋମକୁ ଯାଆନ୍ତୁ"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"ଆପଣଙ୍କ ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"ବଢ଼ିଆ କାମ!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"ଆପଣ ସ୍କ୍ରିନର ତଳ ଧାରରୁ ଉପରକୁ ସ୍ୱାଇପ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"ୱିଣ୍ଡୋକୁ ରିଲିଜ୍ କରିବା ପୂର୍ବରୁ ଅଧିକ ସମୟ ଧରି ରଖିବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ।"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"ଆପଣ ସିଧା ଉପରକୁ ସ୍ୱାଇପ୍ କରି ତା\'ପରେ ବିରତ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"ଯେ କୌଣସି ସମୟରେ ଆପଣଙ୍କ ହୋମ ସ୍କ୍ରିନକୁ ଯିବା ପାଇଁ ଆପଣଙ୍କ ସ୍କିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ଆପଣ ସ୍କ୍ରିନର ତଳ ଧାରରୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"ୱିଣ୍ଡୋକୁ ରିଲିଜ୍ କରିବା ପୂର୍ବରୁ ଅଧିକ ସମୟ ଧରି ରଖିବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ।"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ଆପଣ ସିଧା ଉପରକୁ ସ୍ୱାଇପ୍ କରି ତା\'ପରେ ବିରତ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"ଜେଶ୍ଚରଗୁଡ଼ିକୁ କିପରି ବ୍ୟବହାର କରାଯିବ ଆପଣ ତାହା ଶିଖିଛନ୍ତି। ଜେଶ୍ଚରଗୁଡ଼ିକୁ ବନ୍ଦ କରିବାକୁ, ସେଟିଂସକୁ ଯାଆନ୍ତୁ।"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"ଆପଣ \'ଆପଗୁଡ଼ିକୁ ସ୍ୱିଚ୍ କରନ୍ତୁ\' ଜେଶ୍ଚର୍ ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"ଆପଣ \'ଆପଗୁଡ଼ିକୁ ସ୍ୱିଚ୍ କରନ୍ତୁ\' ଜେଶ୍ଚର୍ ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ଆପଗୁଡ଼ିକୁ ସ୍ୱିଚ୍ କରିବା ପାଇଁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ଆପ୍ସ ମଧ୍ୟରେ ସୁଇଚ କରିବାକୁ, ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ, ଧରି ରଖନ୍ତୁ, ତା\'ପରେ ରିଲିଜ କରନ୍ତୁ।"</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ଆପଗୁଡ଼ିକ ମଧ୍ୟରେ ସ୍ୱିଚ୍ କରିବାକୁ, ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ, ଧରି ରଖନ୍ତୁ, ତା\'ପରେ ରିଲିଜ୍ କରନ୍ତୁ।"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"ଆପ୍ସ ମଧ୍ୟରେ ସ୍ୱିଚ କରିବାକୁ, 2ଟି ଆଙ୍ଗୁଠିରେ ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ କରି ଧରି ରଖନ୍ତୁ, ତା\'ପରେ ରିଲିଜ କର।"</string> - <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ଆପ୍ସ ସୁଇଚ କରନ୍ତୁ"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"ଆପଣଙ୍କ ସ୍କ୍ରିନ୍ର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରି ଧରି ରଖନ୍ତୁ, ତା\'ପରେ ରିଲିଜ୍ କରନ୍ତୁ"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"ବହୁତ ବଢ଼ିଆ!"</string> + <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ଆପ୍ସ ସ୍ୱିଚ କରନ୍ତୁ"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"ସବୁ ପ୍ରସ୍ତୁତ"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"ହୋଇଗଲା"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ସେଟିଂସ"</string> @@ -86,7 +80,7 @@ <string name="gesture_tutorial_nice" msgid="2936275692616928280">"ବଢ଼ିଆ!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"ଟ୍ୟୁଟୋରିଆଲ୍ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"ସମ୍ପୂର୍ଣ୍ଣ ଭାବେ ପ୍ରସ୍ତୁତ!"</string> - <string name="allset_hint" msgid="459504134589971527">"ହୋମକୁ ଯିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string> + <string name="allset_hint" msgid="459504134589971527">"ମୂଳପୃଷ୍ଠାକୁ ଯିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string> <string name="allset_button_hint" msgid="2395219947744706291">"ଆପଣଙ୍କ ହୋମ ସ୍କ୍ରିନକୁ ଯିବା ପାଇଁ ହୋମ ବଟନରେ ଟାପ କରନ୍ତୁ"</string> <string name="allset_description_generic" msgid="5385500062202019855">"ଆପଣ ଆପଣଙ୍କ <xliff:g id="DEVICE">%1$s</xliff:g> ବ୍ୟବହାର କରିବା ଆରମ୍ଭ କରିବାକୁ ପ୍ରସ୍ତୁତ ଅଛନ୍ତି"</string> <string name="default_device_name" msgid="6660656727127422487">"ଡିଭାଇସ"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ଟାସ୍କବାର ଦେଖାଯାଇଛି"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ଟାସ୍କବାର ଲୁଚାଯାଇଛି"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ନାଭିଗେସନ ବାର"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"ସର୍ବଦା ଟାସ୍କବାର ଦେଖାନ୍ତୁ"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"ନାଭିଗେସନ ମୋଡ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ଟାସ୍କବାର ଡିଭାଇଡର"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ଶୀର୍ଷ/ବାମକୁ ମୁଭ କରନ୍ତୁ"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ନିମ୍ନ/ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{ଅଧିକ #ଟି ଆପ ଦେଖାନ୍ତୁ।}other{ଅଧିକ #ଟି ଆପ୍ସ ଦେଖାନ୍ତୁ।}}"</string> diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml index 88bc4eb233..df9cc35279 100644 --- a/quickstep/res/values-pa/strings.xml +++ b/quickstep/res/values-pa/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ਐਪ ਸੁਝਾਵਾਂ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ਐਪ ਸੁਝਾਵਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ਪੂਰਵ ਅਨੁਮਾਨਿਤ ਐਪ: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"ਆਪਣੇ ਡੀਵਾਈਸ ਨੂੰ ਘੁੰਮਾਓ"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ਕਿਰਪਾ ਕਰਕੇ ਇਸ਼ਾਰਾ ਨੈਵੀਗੇਸ਼ਨ ਟਿਊਟੋਰੀਅਲ ਨੂੰ ਪੂਰਾ ਕਰਨ ਲਈ ਆਪਣੇ ਡੀਵਾਈਸ ਨੂੰ ਘੁੰਮਾਓ"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸੱਜੇ ਜਾਂ ਖੱਬੇ ਪਾਸੇ ਦੇ ਬਿਲਕੁਲ ਕਿਨਾਰੇ ਤੋਂ ਸਵਾਈਪ ਕਰਦੇ ਹੋ"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸੱਜੇ ਜਾਂ ਖੱਬੇ ਕਿਨਾਰੇ ਤੋਂ ਸਕ੍ਰੀਨ ਦੇ ਵਿਚਕਾਰ ਤੱਕ ਸਵਾਈਪ ਕਰਦੇ ਹੋ ਅਤੇ ਛੱਡ ਦਿੰਦੇ ਹੋ"</string> - <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"ਤੁਸੀਂ ਪਿੱਛੇ ਜਾਣ ਲਈ ਸੱਜੇ ਪਾਸੇ ਤੋਂ ਸਵਾਈਪ ਕਰਨ ਦਾ ਤਰੀਕਾ ਜਾਣਿਆ। ਅੱਗੇ, ਐਪਾਂ ਵਿਚਾਲੇ ਸਵਿੱਚ ਕਰਨ ਦਾ ਤਰੀਕਾ ਜਾਣੋ।"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"ਤੁਸੀਂ \'ਵਾਪਸ ਜਾਓ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਲੇ ਹਿੱਸੇ ਦੇ ਬਹੁਤ ਨੇੜੇ ਸਵਾਈਪ ਨਾ ਕਰੋ"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸੱਜੇ ਜਾਂ ਖੱਬੇ ਪਾਸੇ ਦੇ ਬਿਲਕੁਲ ਕਿਨਾਰੇ ਤੋਂ ਸਵਾਈਪ ਕਰਦੇ ਹੋ।"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸੱਜੇ ਜਾਂ ਖੱਬੇ ਕਿਨਾਰੇ ਤੋਂ ਸਕ੍ਰੀਨ ਦੇ ਵਿਚਕਾਰ ਤੱਕ ਸਵਾਈਪ ਕਰਦੇ ਹੋ ਅਤੇ ਛੱਡ ਦਿੰਦੇ ਹੋ।"</string> + <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"ਤੁਸੀਂ ਪਿੱਛੇ ਜਾਣ ਲਈ ਸੱਜੇ ਪਾਸੇ ਤੋਂ ਸਵਾਈਪ ਕਰਨ ਦਾ ਤਰੀਕਾ ਜਾਣਿਆ। ਅੱਗੇ, ਐਪਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਦਾ ਤਰੀਕਾ ਜਾਣੋ।"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"ਤੁਸੀਂ \'ਵਾਪਸ ਜਾਓ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ।"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਲੇ ਹਿੱਸੇ ਦੇ ਬਹੁਤ ਨੇੜੇ ਸਵਾਈਪ ਨਾ ਕਰੋ।"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ਪਿੱਛੇ ਜਾਣ ਦੇ ਸੰਕੇਤ ਦੀ ਸੰਵੇਦਨਸ਼ੀਲਤਾ ਬਦਲਣ ਲਈ, ਸੈਟਿੰਗਾਂ \'ਤੇ ਜਾਓ"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"ਪਿੱਛੇ ਜਾਣ ਲਈ ਸਵਾਈਪ ਕਰੋ"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ਪਿਛਲੀ ਸਕ੍ਰੀਨ \'ਤੇ ਵਾਪਸ ਜਾਣ ਲਈ, ਖੱਬੇ ਜਾਂ ਸੱਜੇ ਕਿਨਾਰੇ ਤੋਂ ਸਕ੍ਰੀਨ ਦੇ ਵਿਚਕਾਰ ਤੱਕ ਸਵਾਈਪ ਕਰੋ।"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"ਪਿਛਲੀ ਸਕ੍ਰੀਨ \'ਤੇ ਵਾਪਸ ਜਾਣ ਲਈ, ਖੱਬੇ ਜਾਂ ਸੱਜੇ ਕਿਨਾਰੇ ਤੋਂ ਸਕ੍ਰੀਨ ਦੇ ਵਿਚਕਾਰ ਤੱਕ 2 ਉਂਗਲਾਂ ਨਾਲ ਸਵਾਈਪ ਕਰੋ।"</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"ਵਾਪਸ ਜਾਓ"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"ਖੱਬੇ ਜਾਂ ਸੱਜੇ ਕਿਨਾਰੇ ਤੋਂ ਸਕ੍ਰੀਨ ਦੇ ਵਿਚਕਾਰ ਤੱਕ ਸਵਾਈਪ ਕਰੋ"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਲੇ ਕਿਨਾਰੇ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"ਪੱਕਾ ਕਰੋ ਕਿ ਸਕ੍ਰੀਨ ਨੂੰ ਛੱਡਣ ਤੋਂ ਪਹਿਲਾਂ ਰੁਕੋ ਨਾ"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਿੱਧਾ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"ਤੁਸੀਂ \'ਹੋਮ \'ਤੇ ਜਾਓ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ। ਅੱਗੇ, ਜਾਣੋ ਕਿ ਪਿੱਛੇ ਕਿਵੇਂ ਜਾਣਾ ਹੈ।"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"ਤੁਸੀਂ \'ਹੋਮ \'ਤੇ ਜਾਓ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਲੇ ਕਿਨਾਰੇ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰਦੇ ਹੋ।"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ਪੱਕਾ ਕਰੋ ਕਿ ਸਕ੍ਰੀਨ ਨੂੰ ਛੱਡਣ ਤੋਂ ਪਹਿਲਾਂ ਰੁਕੋ ਨਾ।"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਿੱਧੇ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰਦੇ ਹੋ।"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"ਤੁਸੀਂ \'ਹੋਮ \'ਤੇ ਜਾਓ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ। ਅੱਗੇ, ਜਾਣੋ ਕਿ ਪਿੱਛੇ ਕਿਵੇਂ ਜਾਣਾ ਹੈ।"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"ਤੁਸੀਂ \'ਹੋਮ \'ਤੇ ਜਾਓ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ।"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"ਹੋਮ \'ਤੇ ਜਾਣ ਲਈ ਸਵਾਈਪ ਕਰੋ"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ਆਪਣੀ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ। ਇਹ ਇਸ਼ਾਰਾ ਹਮੇਸ਼ਾਂ ਤੁਹਾਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਲੈ ਜਾਂਦਾ ਹੈ।"</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ 2 ਉਂਗਲਾਂ ਨਾਲ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ। ਇਹ ਇਸ਼ਾਰਾ ਹਮੇਸ਼ਾਂ ਤੁਹਾਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਲੈ ਜਾਂਦਾ ਹੈ।"</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਜਾਓ"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"ਆਪਣੀ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"ਬਹੁਤ ਵਧੀਆ!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਲੇ ਕਿਨਾਰੇ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"ਛੱਡਣ ਤੋਂ ਪਹਿਲਾਂ ਵਿੰਡੋ ਨੂੰ ਕੁਝ ਸਮੇਂ ਲਈ ਦਬਾ ਕੇ ਰੱਖੋ"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਿੱਧਾ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ, ਫਿਰ ਰੋਕੋ"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"ਕਿਸੇ ਵੀ ਸਮੇਂ ਆਪਣੀ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਜਾਣ ਲਈ, ਆਪਣੀ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਲੇ ਕਿਨਾਰੇ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰਦੇ ਹੋ।"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"ਛੱਡਣ ਤੋਂ ਪਹਿਲਾਂ ਵਿੰਡੋ ਨੂੰ ਕੁਝ ਸਮੇਂ ਲਈ ਦਬਾ ਕੇ ਰੱਖੋ।"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਿੱਧੇ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰਦੇ ਹੋ, ਫਿਰ ਰੋਕੋ।"</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"ਤੁਸੀਂ ਇਸ਼ਾਰੇ ਵਰਤਣ ਬਾਰੇ ਜਾਣਿਆ। ਇਸ਼ਾਰੇ ਬੰਦ ਕਰਨ ਲਈ, ਸੈਟਿੰਗਾਂ \'ਤੇ ਜਾਓ।"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"ਤੁਸੀਂ \'ਐਪਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਰੋ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"ਤੁਸੀਂ \'ਐਪਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਰੋ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ।"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ਐਪਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ ਸਵਾਈਪ ਕਰੋ"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ਐਪਾਂ ਵਿਚਾਲੇ ਸਵਿੱਚ ਕਰਨ ਲਈ, ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ ਉੱਤੇ ਸਵਾਈਪ ਕਰ ਕੇ ਦਬਾਈ ਰੱਖੋ ਅਤੇ ਫਿਰ ਛੱਡੋ।"</string> - <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"ਐਪਾਂ ਵਿਚਾਲੇ ਸਵਿੱਚ ਕਰਨ ਲਈ, ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ 2 ਉਂਗਲਾਂ ਨਾਲ ਉੱਤੇ ਸਵਾਈਪ ਕਰ ਕੇ ਦਬਾਈ ਰੱਖੋ ਅਤੇ ਫਿਰ ਛੱਡੋ।"</string> - <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ਐਪਾਂ ਸਵਿੱਚ ਕਰੋ"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"ਆਪਣੀ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰ ਕੇ ਦਬਾਈ ਰੱਖੋ, ਅਤੇ ਫਿਰ ਛੱਡੋ"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"ਬਹੁਤ ਵਧੀਆ!"</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ਐਪਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ, ਆਪਣੀ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰਕੇ ਦਬਾਈ ਰੱਖੋ ਅਤੇ ਫਿਰ ਛੱਡੋ।"</string> + <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"ਐਪਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ, ਆਪਣੀ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ 2 ਉਂਗਲਾਂ ਨਾਲ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰ ਕੇ ਦਬਾਈ ਰੱਖੋ ਅਤੇ ਫਿਰ ਛੱਡੋ।"</string> + <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ਐਪਾਂ ਵਿਚਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"ਪੂਰੀ ਤਰ੍ਹਾਂ ਤਿਆਰ"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"ਹੋ ਗਿਆ"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ਸੈਟਿੰਗਾਂ"</string> @@ -103,7 +97,7 @@ <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ਛੱਡੋ"</string> <string name="accessibility_rotate_button" msgid="4771825231336502943">"ਸਕ੍ਰੀਨ ਘੁਮਾਓ"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"ਟਾਸਕਬਾਰ ਸਿੱਖਿਆ"</string> - <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"ਇੱਕੋ ਸਮੇਂ \'ਤੇ 2 ਐਪਾਂ ਵਰਤਣ ਲਈ, ਐਪ ਨੂੰ ਪਾਸੇ ਵੱਲ ਘਸੀਟੋ"</string> + <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"ਇੱਕ ਵਾਰ ਵਿੱਚ 2 ਐਪਾਂ ਵਰਤਣ ਲਈ, ਐਪ ਨੂੰ ਪਾਸੇ ਵੱਲ ਘਸੀਟੋ"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"ਟਾਸਕਬਾਰ ਦਿਖਾਉਣ ਲਈ ਥੋੜ੍ਹਾ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"ਤੁਹਾਡੇ ਨਿਯਮਬੱਧ ਕੰਮ ਦੇ ਆਧਾਰ \'ਤੇ ਐਪ ਸੁਝਾਅ ਪ੍ਰਾਪਤ ਕਰੋ"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"ਟਾਸਕਬਾਰ ਨੂੰ ਸਵੈ-ਲੁਕਾਉਣ ਲਈ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਇਸ਼ਾਰਾ ਨੈਵੀਗੇਸ਼ਨ ਨੂੰ ਚਾਲੂ ਕਰੋ"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ਟਾਸਕਬਾਰ ਨੂੰ ਦਿਖਾਇਆ ਗਿਆ"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ਟਾਸਕਬਾਰ ਨੂੰ ਲੁਕਾਇਆ ਗਿਆ"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ਨੈਵੀਗੇਸ਼ਨ ਵਾਲੀ ਪੱਟੀ"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"ਹਮੇਸ਼ਾਂ ਟਾਸਕਬਾਰ ਦਿਖਾਓ"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"ਨੈਵੀਗੇਸ਼ਨ ਮੋਡ ਬਦਲੋ"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ਟਾਸਕਬਾਰ ਵਿਭਾਜਕ"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ਸਿਖਰਲੇ/ਖੱਬੇ ਪਾਸੇ ਲੈ ਕੇ ਜਾਓ"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ਹੇਠਾਂ/ਸੱਜੇ ਪਾਸੇ ਲੈ ਕੇ ਜਾਓ"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# ਹੋਰ ਐਪ ਦਿਖਾਓ।}one{# ਹੋਰ ਐਪ ਦਿਖਾਓ।}other{# ਹੋਰ ਐਪਾਂ ਦਿਖਾਓ।}}"</string> diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml index 4077212844..f09e41224a 100644 --- a/quickstep/res/values-pl/strings.xml +++ b/quickstep/res/values-pl/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Włączono sugestie aplikacji"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sugestie aplikacji są wyłączone"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Przewidywana aplikacja: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Obróć urządzenie"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Obróć urządzenie, aby ukończyć samouczek nawigacji przy użyciu gestów"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Pamiętaj, aby przesuwać palcem od samej krawędzi (prawej lub lewej)"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Pamiętaj, aby przesuwać palcem od prawej lub lewej krawędzi do środka ekranu i podnieść palec"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Pamiętaj, aby przesuwać palcem od samej krawędzi (prawej lub lewej)."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Pamiętaj, aby przesuwać palcem od prawej lub lewej krawędzi do środka ekranu i podnieść palec."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Wiesz już, jak przesuwać palcem, aby przejść wstecz. Poćwicz teraz przełączanie aplikacji."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Gest przejścia wstecz został opanowany"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Pamiętaj, aby nie przesuwać palcem zbyt blisko dolnej części ekranu"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Gest przejścia wstecz został opanowany."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Pamiętaj, aby nie przesuwać palcem zbyt blisko dolnej części ekranu."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Czułość gestu cofania możesz zmienić w Ustawieniach"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Przesuń palcem, aby przejść wstecz"</string> - <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Aby wrócić do poprzedniego ekranu, przesuń palcem od lewej lub prawej krawędzi do środka ekranu."</string> - <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Aby wrócić do poprzedniego ekranu, przesuń 2 palcami od lewej lub prawej krawędzi do środka ekranu."</string> + <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Aby wrócić do ostatniego ekranu, przesuń palcem od lewej lub prawej krawędzi do środka ekranu."</string> + <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Aby wrócić do ostatniego ekranu, przesuń 2 palcami od lewej lub prawej krawędzi do środka ekranu."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Przejście wstecz"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Przesuń palcem od lewej lub prawej krawędzi do środka ekranu"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Pamiętaj, aby przesuwać palcem od dolnej krawędzi ekranu"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Pamiętaj, aby przed podniesieniem palca nie było przerwy"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Pamiętaj, aby przesuwać palcem prosto do góry"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Gest przechodzenia na ekran główny został opanowany. Poćwicz teraz wracanie do poprzedniego ekranu."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Gest przechodzenia na ekran główny został opanowany"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Pamiętaj, aby przesuwać palcem od dolnej krawędzi ekranu."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pamiętaj, aby przed podniesieniem palca nie było przerwy."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Pamiętaj, aby przesuwać palcem prosto do góry."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Potrafisz już przejść na ekran główny. Teraz naucz się, jak wrócić do poprzedniego ekranu."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Gest przechodzenia na ekran główny został opanowany."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Przesuń palcem, aby przejść na ekran główny"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Przesuń palcem od dołu ekranu. Ten gest zawsze powoduje przejście na ekran główny."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Przesuń 2 palcami od dołu ekranu. Ten gest zawsze powoduje przejście do ekranu głównego."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Przejście na ekran główny"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Przesuń palcem z dołu ekranu w górę"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Brawo!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Pamiętaj, aby przesuwać palcem od dolnej krawędzi ekranu"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Przytrzymaj okno dłużej, zanim podniesiesz palec"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Pamiętaj, aby przesuwać palcem prosto do góry, a potem przerwać ruch"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Otwórz ekran główny"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Aby wyświetlić ekran główny w dowolnym momencie, przesuń od dołu do góry ekranu"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Pamiętaj, aby przesuwać palcem od dolnej krawędzi ekranu."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Przytrzymaj okno dłużej, zanim podniesiesz palec."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Pamiętaj, aby przesuwać palcem prosto do góry, a potem przerwać ruch."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Wiesz już, jak używać gestów. Aby wyłączyć gesty, przejdź do Ustawień."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Gest przełączania aplikacji został opanowany"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Gest przełączania aplikacji został opanowany."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Przesuń palcem, aby przełączać aplikacje"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Aby przełączać się między aplikacjami, przesuń palcem od dołu do góry ekranu, przytrzymaj i puść."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Aby przełączać się między aplikacjami, przesuń palcem od dołu ekranu, przytrzymaj i puść."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Aby przełączać się między aplikacjami, przesuń 2 palcami od dołu ekranu, przytrzymaj i puść."</string> - <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Przełącz aplikację"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Przesuń palcem z dołu ekranu w górę, przytrzymaj i puść"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Brawo!"</string> + <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Przełączanie aplikacji"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Wszystko gotowe"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Gotowe"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Ustawienia"</string> @@ -94,7 +88,7 @@ <string name="action_share" msgid="2648470652637092375">"Udostępnij"</string> <string name="action_screenshot" msgid="8171125848358142917">"Zrzut ekranu"</string> <string name="action_split" msgid="2098009717623550676">"Podziel"</string> - <string name="toast_split_select_app" msgid="8464310533320556058">"Aby podzielić ekran, kliknij drugą aplikację"</string> + <string name="toast_split_select_app" msgid="8464310533320556058">"Kliknij drugą aplikację, aby podzielić ekran"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Wybierz drugą aplikację, aby podzielić ekran"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Nie możesz wykonać tego działania, bo nie zezwala na to aplikacja lub Twoja organizacja"</string> <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Pominąć samouczek nawigacji?"</string> @@ -104,7 +98,7 @@ <string name="accessibility_rotate_button" msgid="4771825231336502943">"Obróć ekran"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Informacje o pasku aplikacji"</string> <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Przeciągnij aplikację w bok, aby używać 2 aplikacji naraz"</string> - <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Aby wyświetlić pasek aplikacji, przesuń palcem krótko w górę"</string> + <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Przesuń palcem krótko w górę, aby wyświetlić pasek aplikacji"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Otrzymuj sugestie aplikacji na podstawie rutyny"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Włącz nawigację przy użyciu gestów w Ustawieniach, aby automatycznie ukrywać pasek aplikacji"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Wykorzystaj potencjał paska aplikacji"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Pasek aplikacji widoczny"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Pasek aplikacji ukryty"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Pasek nawigacyjny"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Zawsze pokazuj pasek aplikacji"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Zmień tryb nawigacji"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Linia dzielenia paska aplikacji"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Przesuń w górny lewy róg"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Przesuń w dolny prawy róg"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Pokaż jeszcze # aplikację.}few{Pokaż jeszcze # aplikacje.}many{Pokaż jeszcze # aplikacji.}other{Pokaż jeszcze # aplikacji.}}"</string> diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml index a8867996de..2b287b3bdf 100644 --- a/quickstep/res/values-pt-rPT/strings.xml +++ b/quickstep/res/values-pt-rPT/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugestões de apps ativadas"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"As sugestões de apps estão desativadas"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App prevista: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rode o dispositivo"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rode o seu dispositivo para concluir o tutorial de navegação por gestos"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Deslize rapidamente a partir da extremidade mais à direita ou mais à esquerda"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Deslize rapidamente a partir da extremidade esquerda ou direita até ao centro do ecrã e solte"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Deslize rapidamente a partir da extremidade mais à direita ou mais à esquerda."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Deslize rapidamente a partir da extremidade esquerda ou direita até ao centro do ecrã e solte."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Aprendeu a deslizar a partir da direita para retroceder. A seguir, saiba como alternar entre apps."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Concluiu o gesto para retroceder"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Garanta que não desliza rapidamente com o dedo demasiado perto da parte inferior do ecrã"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Concluiu o gesto para retroceder."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Garanta que não desliza rapidamente com o dedo demasiado perto da parte inferior do ecrã."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Altere a sensibilidade do gesto para voltar nas Definições."</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Deslize rapidamente com o dedo para retroceder"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Para voltar ao último ecrã, deslize rapidamente do limite esquerdo ou direito até ao centro do ecrã."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Para voltar ao último ecrã, deslize rapidamente com 2 dedos a partir da extremidade esquerda ou direita até ao centro do ecrã."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Voltar"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Deslize rapidamente a partir da extremidade esquerda ou direita para o meio do ecrã"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Deslize rapidamente com o dedo a partir do limite inferior do ecrã"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Não faça uma pausa antes de soltar"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Deslize rapidamente com o dedo para cima"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Concluiu o gesto para aceder ao ecrã principal. A seguir, saiba como retroceder."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Concluiu o gesto para aceder ao ecrã principal"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Garanta que desliza rapidamente com o dedo a partir do limite inferior do ecrã."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Garanta que não faz uma pausa antes de soltar."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Garanta que desliza rapidamente com o dedo para cima."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Concluiu o gesto para aceder ao ecrã principal. A seguir, saiba como retroceder."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Concluiu o gesto para aceder ao ecrã principal."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Deslize rapidamente com o dedo para aceder ao ecrã principal"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Deslize rapidamente para cima a partir da parte inferior. Este gesto abre sempre o ecrã principal."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Deslize rapidamente para cima com 2 dedos no fundo do ecrã. Este gesto abre sempre o ecrã principal."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Aceda ao ecrã principal"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Deslize rapidamente para cima a partir da parte inferior do ecrã"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Muito bem!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Deslize rapidamente com o dedo a partir do limite inferior do ecrã"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Experimente premir a janela durante mais tempo antes de soltar"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Garanta que desliza rapidamente com o dedo para cima e, em seguida, faz uma pausa"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Para aceder ao ecrã principal em qualquer altura, deslize rapidamente de baixo para cima no seu ecrã"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Deslize rapidamente com o dedo a partir do limite inferior do ecrã."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Experimente premir a janela durante mais tempo antes de soltar."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Garanta que desliza rapidamente com o dedo para cima e, em seguida, faz uma pausa."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Aprendeu a utilizar gestos. Para desativar os gestos, aceda às Definições."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Concluiu o gesto para alternar entre apps"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Concluiu o gesto para alternar entre apps."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Deslize rapidamente com o dedo para alternar entre apps"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Para alternar entre apps, deslize para cima sem soltar a partir da parte inferior do ecrã e solte."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Para mudar de app, deslize rapidamente para cima com 2 dedos sem soltar no fundo do ecrã e solte."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Mude de app"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Deslize rapidamente para cima a partir da parte inferior do ecrã sem soltar e, em seguida, solte"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Muito bem!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Está tudo pronto"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Concluído"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Definições"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barra de tarefas apresentada"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barra de tarefas ocultada"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegação"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Mostr. sempre Barra de tarefas"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Alterar modo de navegação"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divisor da Barra de tarefas"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover para a parte superior esquerda"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover para a part superior direita"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Mostrar mais # app.}other{Mostrar mais # apps.}}"</string> diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml index 1430885981..4092c06690 100644 --- a/quickstep/res/values-pt/strings.xml +++ b/quickstep/res/values-pt/strings.xml @@ -23,7 +23,7 @@ <string name="recent_task_option_freeform" msgid="48863056265284071">"Forma livre"</string> <string name="recents_empty_message" msgid="7040467240571714191">"Nenhum item recente"</string> <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configurações de uso do app"</string> - <string name="recents_clear_all" msgid="5328176793634888831">"Remover tudo"</string> + <string name="recents_clear_all" msgid="5328176793634888831">"Limpar tudo"</string> <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recentes"</string> <string name="task_view_closed" msgid="9170038230110856166">"Tarefa encerrada"</string> <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string> @@ -44,45 +44,39 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"O recurso \"sugestões de apps\" está ativado"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"O recurso \"sugestões de apps\" está desativado"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App previsto: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Gire o dispositivo"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Gire o dispositivo para concluir o tutorial da navegação por gestos"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Deslize da borda direita ou esquerda"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Deslize da borda direita ou esquerda até o meio da tela e solte"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Deslize da borda direita ou esquerda."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Deslize da borda direita ou esquerda até o meio da tela e solte."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Você aprendeu a deslizar da direita para voltar. A seguir, aprenda a trocar de app."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Você concluiu o gesto para voltar"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Não deslize perto demais da parte inferior da tela"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Você concluiu o gesto para voltar."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Não deslize perto demais da parte inferior da tela."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Mude a sensibilidade do gesto de voltar nas configurações"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Deslize para voltar"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Para voltar à tela anterior, deslize da borda esquerda ou direita até o meio da tela."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Para voltar à tela anterior, deslize da borda esquerda ou direita até o meio da tela com dois dedos."</string> - <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Volte"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Deslize da borda esquerda ou direita até o meio da tela"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Deslize da borda inferior da tela para cima"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Não pare antes de soltar"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Deslize para cima em linha reta"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Você concluiu o gesto para acessar a tela inicial. Agora, aprenda a voltar."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Você concluiu o gesto para acessar a tela inicial"</string> + <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Voltar"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Deslize da borda inferior da tela para cima."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Não pare antes de soltar."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Deslize para cima."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Você concluiu o gesto para acessar a tela inicial. A seguir, aprenda a voltar."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Você concluiu o gesto para acessar a tela inicial."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Deslizar para voltar à tela inicial"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Deslize de baixo para cima na tela. Esse gesto sempre leva você para a tela inicial."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Deslize de baixo para cima na tela com dois dedos. Esse gesto sempre leva você para a tela inicial."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Vá para a página inicial"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Deslize de baixo para cima na tela"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Muito bem!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Deslize da borda inferior da tela para cima"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Mantenha a janela pressionada por mais tempo antes de soltar"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Deslize para cima e pare"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Ir para a página inicial"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Para acessar sua tela inicial a qualquer momento, deslize de baixo para cima na tela"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Deslize da borda inferior da tela para cima."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Mantenha a janela pressionada por mais tempo antes de soltar."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Deslize para cima e pare."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Você aprendeu. Para desativar os gestos, acesse as Configurações."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Você concluiu o gesto para mudar de app"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Você concluiu o gesto para trocar de app."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Deslizar para trocar de app"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Para mudar de app, deslize de baixo para cima, mantenha a tela pressionada por um tempo e solte."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Para mudar de app, deslize de baixo para cima na tela com dois dedos, segure por um tempo e solte."</string> - <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Mude de app"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Deslize de baixo para cima na tela, segure e depois solte"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Muito bem!"</string> + <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Alternar entre apps"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Tudo pronto"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Concluído"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Configurações"</string> - <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Tente de novo"</string> + <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Tentar novamente"</string> <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Muito bem!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"Tudo pronto!"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barra de tarefas visível"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barra de tarefas oculta"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegação"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Sempre mostrar a Barra de tarefas"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Mudar o modo de navegação"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Separador da Barra de tarefas"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover para cima/para a esquerda"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover para baixo/para a direita"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Mostrar mais # app.}one{Mostrar mais # app.}other{Mostrar mais # apps.}}"</string> diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml index 194859945d..003b867815 100644 --- a/quickstep/res/values-ro/strings.xml +++ b/quickstep/res/values-ro/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugestiile de aplicații au fost activate"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sugestiile de aplicații au fost dezactivate"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplicația estimată: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotește dispozitivul"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rotește dispozitivul pentru a încheia tutorialul de navigare prin gesturi"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Glisează dinspre marginea dreaptă îndepărtată sau dinspre marginea stângă îndepărtată"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Glisează dinspre marginea dreaptă sau stângă spre mijlocul ecranului și eliberează"</string> - <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Ai învățat să revii la ecranul anterior glisând din dreapta. Acum învață să comuți între aplicații."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Ai finalizat gestul „înapoi”"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Nu glisa prea aproape de partea de jos a ecranului"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Glisează dinspre marginea dreaptă îndepărtată sau dinspre marginea stângă îndepărtată."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Glisează dinspre marginea dreaptă sau stângă spre mijlocul ecranului și eliberează."</string> + <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Ai învățat cum să glisezi din dreapta pentru a reveni. Acum află cum să comuți aplicațiile."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Ai finalizat gestul „înapoi”."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Nu glisa prea aproape de partea de jos a ecranului."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Schimbă sensibilitatea gestului „Înapoi” accesând Setările"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Glisează pentru a reveni"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Pentru a reveni la ultimul ecran, glisează de la marginea stângă sau dreaptă spre mijlocul ecranului."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Pentru a reveni la ultimul ecran, glisează cu două degete dinspre marginea stângă sau dreaptă spre mijlocul ecranului."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Înapoi"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Glisează dinspre marginea stângă sau dreaptă până la jumătatea ecranului"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Glisează în sus dinspre marginea de jos a ecranului"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Nu întrerupe gestul înainte de a elibera"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Glisează direct în sus"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Ai finalizat gestul „accesează ecranul de pornire”. În continuare, află cum să revii."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Ai finalizat gestul „accesează ecranul de pornire”"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Glisează în sus dinspre marginea de jos a ecranului."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Nu întrerupe gestul înainte de a elibera."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Glisează direct în sus."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Ai finalizat gestul „accesează ecranul de pornire”. Acum află cum să revii."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Ai finalizat gestul „accesează ecranul de pornire”."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Glisează pentru a accesa ecranul de pornire"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Glisează în sus din partea de jos a ecranului. Cu acest gest accesezi mereu ecranul de pornire."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Glisează în sus cu două degete din partea de jos. Cu acest gest accesezi mereu ecranul de pornire."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Înapoi la ecranul de pornire"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Glisează în sus din partea de jos a ecranului"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Excelent!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Glisează în sus dinspre marginea de jos a ecranului"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Încearcă să ții fereastra mai mult înainte s-o eliberezi"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Glisează direct în sus, apoi întrerupe"</string> - <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Ai învățat să folosești gesturi. Pentru a dezactiva gesturile, accesează setările."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Ai finalizat gestul „comută între aplicații”"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Pentru a accesa oricând ecranul de pornire, glisează în sus din partea de jos a ecranului"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Glisează în sus dinspre marginea de jos a ecranului."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Încearcă să ții fereastra mai mult înainte s-o eliberezi."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Glisează direct în sus, apoi întrerupe."</string> + <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Ai învățat să folosești gesturi. Pentru a dezactiva gesturile, accesează Setările."</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Ai finalizat gestul „comută între aplicații”."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Glisează pentru a comuta între aplicații"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Ca să comuți între aplicații, glisează de jos în sus, ține degetul pe ecran, apoi ridică-l."</string> - <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Ca să comuți între aplicații, glisează cu 2 degete de jos în sus, ține-le pe ecran, apoi ridică-le."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Ca să comuți între aplicații, glisează în sus din partea de jos a ecranului, așteaptă și eliberează."</string> + <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Ca să comuți între aplicații, glisează cu două degete de jos în sus, așteaptă și eliberează"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Comută între aplicații"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Glisează în sus din partea de jos a ecranului, ține apăsat, apoi eliberează"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Felicitări!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Gata"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Gata"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Setări"</string> @@ -86,7 +80,7 @@ <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bravo!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorialul <xliff:g id="CURRENT">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"Gata!"</string> - <string name="allset_hint" msgid="459504134589971527">"Glisează în sus pentru a accesa ecranul principal"</string> + <string name="allset_hint" msgid="459504134589971527">"Glisați în sus pentru a accesa pagina principală"</string> <string name="allset_button_hint" msgid="2395219947744706291">"Atinge butonul ecran de pornire ca să accesezi ecranul de pornire"</string> <string name="allset_description_generic" msgid="5385500062202019855">"Ești gata să folosești <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="default_device_name" msgid="6660656727127422487">"dispozitivul"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Bara de activități este afișată"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Bara de activități este ascunsă"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Bară de navigare"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Afișează întotdeauna bara de activități"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Schimbă modul de navigare"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Separator pentru bara de activități"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mută în stânga sus"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mută în dreapta jos"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Afișează încă # aplicație}few{Afișează încă # aplicații}other{Afișează încă # de aplicații}}"</string> diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml index c62fccaafb..166fe7c66f 100644 --- a/quickstep/res/values-ru/strings.xml +++ b/quickstep/res/values-ru/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Функция \"Рекомендуемые приложения\" включена."</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Функция \"Рекомендуемые приложения\" отключена."</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Рекомендуемое приложение: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Поверните устройство"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Чтобы перейти к руководству по жестам, нужно повернуть устройство."</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Проведите справа налево или слева направо от самого края экрана."</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Проведите от правого или левого края экрана к центру и отпустите палец."</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Проведите справа налево или слева направо от самого края экрана."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Проведите от правого или левого края экрана до середины дисплея и отпустите палец."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Теперь вы знаете, как вернуться, проведя справа налево. Далее мы расскажем, как переключаться между приложениями."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Вы выполнили жест для возврата на предыдущий экран."</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Проведите пальцем не слишком близко к нижнему краю экрана."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Вы выполнили жест для перехода назад."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Проведите пальцем не слишком близко к нижнему краю экрана."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Уровень чувствительности можно изменить в настройках."</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Возврат к предыдущему экрану"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Чтобы вернуться к предыдущему экрану, проведите от левого или правого края дисплея к центру."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Чтобы вернуться на предыдущий экран, проведите двумя пальцами от левого или правого края экрана к центру."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Как вернуться к предыдущему экрану"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Проведите от левого или правого края экрана к центру."</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Проведите снизу вверх от самого края экрана."</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Прежде чем отпустить палец, не задерживайте его в одной точке."</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Проведите по экрану ровно вверх."</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Вы выполнили жест для перехода на главный экран. Далее мы расскажем, как возвращаться назад."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Вы выполнили жест для перехода на главный экран."</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Проведите снизу вверх от самого края экрана."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Не приостанавливайтесь перед тем, как отпустить палец."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Проведите по экрану ровно вверх."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Вы выполнили жест для перехода на главный экран. Далее мы расскажем, как вернуться назад."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Вы выполнили жест для перехода на главный экран."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Переход на главный экран"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Проведите вверх от нижнего края дисплея. Этот жест открывает главный экран."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Проведите двумя пальцами вверх от нижнего края экрана. Этот жест открывает главный экран."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Как перейти на главный экран"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Проведите вверх от нижнего края экрана."</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"У вас получилось!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Проведите снизу вверх от самого края экрана."</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Прежде чем отпустить палец, задержите его на экране немного дольше."</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Проведите по экрану ровно вверх и задержите палец в конце."</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Переход на главный экран"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Чтобы открыть главный экран, проведите снизу вверх по экрану."</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Проведите снизу вверх от самого края экрана."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Прежде чем отпускать палец, задержите его на дисплее подольше."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Проведите по экрану ровно вверх, а затем задержите палец в крайнем положении."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Теперь вы знаете, как использовать жесты. Чтобы отключить их, перейдите в настройки."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Вы выполнили жест для переключения между приложениями."</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Вы выполнили жест для переключения между приложениями."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Переключение между приложениями"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Чтобы переключиться между приложениями‚ проведите по экрану снизу вверх, задержите палец, а затем отпустите."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Чтобы сменить приложение, проведите двумя пальцами снизу вверх, задержите пальцы, а затем отпустите."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Как переключаться между приложениями"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Проведите вверх от нижнего края экрана, задержите палец в одной точке и отпустите."</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Отлично!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Готово"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Готово"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Настройки"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Панель задач показана"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Панель задач скрыта"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Панель навигации"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Всегда показывать панель задач"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Изменить режим навигации"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Разделитель панели задач"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Переместить вверх или влево"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Переместить вниз или вправо"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Показать ещё # приложение}one{Показать ещё # приложение}few{Показать ещё # приложения}many{Показать ещё # приложений}other{Показать ещё # приложения}}"</string> diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml index 2c1e67d362..1b850efe90 100644 --- a/quickstep/res/values-si/strings.xml +++ b/quickstep/res/values-si/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"යෙදුම් යෝජනා සබලිතයි"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"යෙදුම් යෝජනා අබල කර ඇත"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"පුරෝකථනය කළ යෙදුම: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"ඔබේ උපාංගය කරකවන්න"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"අභින සංචාලන නිබන්ධනය සම්පූර්ණ කිරීම සඳහා ඔබේ උපාංගය කරකවන්න"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ඔබ ඈත දකුණු හෝ ඈත වම් දාරයේ සිට ස්වයිප් කරන බව සහතික කර ගන්න"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"ඔබ දකුණු හෝ වම් දාරයේ සිට තිරයේ මැදට ස්වයිප් කර අත හරින බව සහතික කර ගන්න"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"ඔබ ඈත දකුණු හෝ ඈත වම් දාරයේ ස්වයිප් කරන බව සහතික කර ගන්න."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"ඔබ දකුණු හෝ වම් දාරයේ සිට තිරයේ මැදට ස්වයිප් කර අත හරින බව සහතික කර ගන්න."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"ආපසු යාමට දකුණේ සිට ස්වයිප් කරන්නේ කෙසේදැයි ඔබ දැන ගත්තේය. ඊළඟට, යෙදුම් මාරු කරන ආකාරය දැන ගන්න."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"ඔබ ආපසු යාමේ ඉංගිතය සම්පූර්ණ කළා"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"ඔබ තිරයේ පහළට ඉතාම සමීපව ස්වයිප් නොකරන බවට සහතික කර ගන්න"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"ඔබ ආපසු යාමේ ඉංගිතය සම්පූර්ණ කරන ලදි."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ඔබ තිරයේ පහළට ඉතාම සමීපව ස්වයිප් නොකරන බවට සහතික කර ගන්න."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ආපසු ඉංගිතයෙහි සංවේදීතාව වෙනස් කිරීමට, සැකසීම් වෙත යන්න"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"ආපසු යාමට ස්වයිප් කරන්න"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"අවසාන තිරයට ආපසු යාමට, වම් හෝ දකුණු දාරයෙන් තිරයේ මැදට ස්වයිප් කරන්න."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"අවසාන තිරයට ආපසු යාමට, වම් හෝ දකුණු දාරයෙන් තිරයේ මැදට ඇඟිලි 2කින් ස්වයිප් කරන්න."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"ආපසු යන්න"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"වම් හෝ දකුණු කෙළවරේ සිට තිරයේ මැදට ස්වයිප් කරන්න"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"ඔබ තිරයේ පහළ දාරයේ සිට ඉහළට ස්වයිප් කරන බව සහතික කර ගන්න"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"යාමට ඉඩ හැරීමට පෙර ඔබ විරාමයක් නොගන්නා බව සහතික කර ගන්න"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"ඔබ කෙලින්ම ඉහළට ස්වයිප් කරන බව සහතික කර ගන්න"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"ඔබ මුල් පිටුවට යාමේ ඉංගිතය සම්පූර්ණ කළා. මීළඟට, ආපසු යන ආකාරය දැන ගන්න."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"ඔබ මුල් පිටුවට යාමේ ඉංගිතය සම්පූර්ණ කළා"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ඔබ තිරයේ පහළ දාරයේ සිට ඉහළට ස්වයිප් කරන බව සහතික කර ගන්න."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"යාමට ඉඩ හැරීමට පෙර ඔබ විරාමයක් නොගන්නා බව සහතික කර ගන්න."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ඔබ කෙලින්ම ඉහළට ස්වයිප් කරන බව සහතික කර ගන්න."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"ඔබ මුල් පිටුවට යාමේ ඉංගිතය සම්පූර්ණ කරන ලදි. ඊළඟට, ආපසු යන ආකාරය දැන ගන්න."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"ඔබ මුල් පිටුවට යාමේ ඉංගිතය සම්පූර්ණ කරන ලදි."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"මුල් පිටුවට යාමට ස්වයිප් කරන්න"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ඔබගේ තිරයේ පහළින් උඩට ස්වයිප් කරන්න.මෙම ඉංගිතය සැම විටම ඔබව මුල් තිරයට ගෙන යයි."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"තිරයේ පහළම සිට ඇඟිලි 2කින් ඉහළට ස්වයිප් කරන්න. මෙම ඉංගිතය සැම විටම ඔබව මුල් තිරයට ගෙන යයි."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"මුල් පිටුවට යන්න"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"ඔබේ තිරයේ පහළ සිට උඩට ස්වයිප් කරන්න"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"අනර්ඝ වැඩක්!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"ඔබ තිරයේ පහළ දාරයේ සිට ඉහළට ස්වයිප් කරන බව සහතික කර ගන්න"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"මුදා හැරීමට පෙර කවුළුව වැඩි වේලාවක් රඳවා ගැනීමට උත්සාහ කරන්න"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"ඔබ කෙළින්ම ඉහළට ස්වයිප් කර, පසුව විරාම කරන බව සහතික කර ගන්න"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"ඕනෑම වේලාවක දී ඔබේ මුල් තිරයට යාම සඳහා, ඔබේ තිරයෙහි පහළ සිට ඉහළට ස්වයිප් කරන්න"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ඔබ තිරයේ පහළ දාරයේ සිට ඉහළට ස්වයිප් කරන බව සහතික කර ගන්න."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"මුදා හැරීමට පෙර කවුළුව වැඩි වේලාවක් රඳවා තබා ගැනීමට උත්සාහ කරන්න."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ඔබ කෙලින්ම ඉහළට ස්වයිප් කර, අනතුරුව විරාම කරන බව සහතික කර ගන්න."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"ඔබ ඉංගිත භාවිත කරන ආකාරය දැන ගෙන ඇත. ඉංගිත ක්රියාවිරහිත කිරීමට, සැකසීම් වෙත යන්න."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"ඔබ යෙදුම් මාරු කිරීමේ ඉංගිතය සම්පූර්ණ කළා"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"ඔබ යෙදුම් මාරු කිරීමේ ඉංගිතය සම්පූර්ණ කර ඇත."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"යෙදුම් මාරු කිරීමට ස්වයිප් කරන්න"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"යෙදුම් අතර මාරු වීමට, ඔබගේ තිරයේ පහළම සිට උඩට ස්වයිප් කර, අල්ලාගෙන සිට, අනතුරුව මුදා හරින්න."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"යෙදුම් අතර මාරු වීමට, ඔබගේ තිරයේ පහළම සිට උඩට ඇඟිලි 2කින් ස්වයිප් කර, අල්ලාගෙන සිට, අනතුරුව මුදා හරින්න."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"යෙදුම් මාරු කරන්න"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"ඔබේ තිරයේ පහළ සිට ඉහළට ස්වයිප් කරන්න, රඳවා ගෙන සිට, පසුව මුදා හරින්න"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"හොඳින් කළා!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"සියල්ල සකසා ඇත"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"නිමයි"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"සැකසීම්"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"කාර්ය තීරුව පෙන්වා ඇත"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"කාර්ය තීරුව සඟවා ඇත"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"සංචලන තීරුව"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"සෑම විටම කාර්ය තීරුව පෙන්වන්න"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"සංචාලන ප්රකාරය වෙනස් කරන්න"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"කාර්ය තීරු බෙදනය"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ඉහළ/වම වෙත ගෙන යන්න"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"පහළ/දකුණ වෙත ගෙන යන්න"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{තවත් # යෙදුමක් පෙන්වන්න.}one{තවත් යෙදුම් #ක් පෙන්වන්න.}other{තවත් යෙදුම් #ක් පෙන්වන්න.}}"</string> diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml index b0e859b814..b2dff2e2e6 100644 --- a/quickstep/res/values-sk/strings.xml +++ b/quickstep/res/values-sk/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Návrhy aplikácií zapnuté"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Návrhy aplikácií vypnuté"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predpovedaná aplikácia: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Otočte zariadenie"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Otočte zariadenie a dokončite tak návod, ako navigovať gestami"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Musíte potiahnuť úplne z pravého alebo ľavého okraja"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Musíte potiahnuť z pravého alebo ľavého okraja do stredu obrazovky a potom uvoľniť"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Musíte potiahnuť úplne z pravého alebo ľavého okraja."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Musíte potiahnuť z pravého alebo ľavého okraja do stredu obrazovky a potom uvoľniť."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Naučili ste sa prejsť späť potiahnutím sprava. V ďalšom kroku sa naučíte prepínať aplikácie."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Dokončili ste gesto na prechod späť"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Nesmiete potiahnuť príliš blízko dolnej časti obrazovky"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Dokončili ste gesto na prechod späť."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Nesmiete potiahnuť príliš blízko dolnej časti obrazovky."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Ak chcete zmeniť citlivosť gesta Späť, prejdite do Nastavení"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Prechod späť potiahnutím"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Na poslednú obrazovku prejdete potiahnutím z ľavého alebo pravého okraja do stredu obrazovky."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Na poslednú obrazovku sa vrátite potiahnutím dvoma prstami z ľavého alebo pravého okraja do stredu obrazovky."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Prechod späť"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Potiahnite z ľavého alebo pravého okraja do stredu obrazovky"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Musíte potiahnuť nahor z dolného okraja obrazovky"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Pred uvoľnením nesmiete zastať"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Musíte potiahnuť priamo nahor"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Dokončili ste gesto prechodu na plochu. Teraz sa naučíte, ako sa vrátiť späť."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Dokončili ste gesto prechodu na plochu"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Musíte potiahnuť nahor z dolného okraja obrazovky."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pred uvoľnením nesmiete zastať."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Musíte potiahnuť priamo hore."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Dokončili ste gesto na prechod na plochu. V ďalšom kroku sa naučíte, ako sa vrátiť späť."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Dokončili ste gesto na prechod na plochu."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Prechod na plochu potiahnutím"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Potiahnite nahor zdola obrazovky. Týmto gestom sa vždy vrátite na plochu."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Postiahnite dvoma prstami z dolnej časti obrazovky. Týmto gestom sa vždy vrátite na plochu."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Prechod na plochu"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Potiahnite z dolnej časti obrazovky nahor"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Skvelé!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Musíte potiahnuť nahor z dolného okraja obrazovky"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Skúste okno pred uvoľnením podržať dlhšie"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Musite potiahnuť priamo nahor a potom zastať"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Prejdenie na plochu"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Na plochu môžete kedykoľvek prejsť potiahnutím nahor z dolnej časti obrazovky"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Musíte potiahnuť nahor z dolného okraja obrazovky."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Skúste okno pred uvoľnením podržať dlhšie."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Musite potiahnuť priamo hore a potom zastať."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Naučili ste sa používať gestá. Gestá môžete vypnúť v nastaveniach."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Dokončili ste gesto na prepnutie aplikácií"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Dokončili ste gesto na prepnutie aplikácií."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Prepínanie aplikácií potiahnutím"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Aplikácie môžete prepínať potiahnutím obrazovky zdola nahor, pridržaním a následným uvoľnením."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Aplikácie prepnete potiahnutím dvoma prstami z dolnej časti obrazovky, ich pridržaním a uvoľnením."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Prepnutie aplikácií"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Potiahnite nahor z dolného okraja obrazovky, pridržte a uvoľnite"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Výborne"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Hotovo"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Hotovo"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Nastavenia"</string> @@ -94,7 +88,7 @@ <string name="action_share" msgid="2648470652637092375">"Zdieľať"</string> <string name="action_screenshot" msgid="8171125848358142917">"Snímka obrazovky"</string> <string name="action_split" msgid="2098009717623550676">"Rozdeliť"</string> - <string name="toast_split_select_app" msgid="8464310533320556058">"Obrazovku rozdelíte klepnutím na inú aplikáciu"</string> + <string name="toast_split_select_app" msgid="8464310533320556058">"Rozdelenú obrazovku spustíte klep. na inú aplik."</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Na použitie rozd. obrazovky vyberte inú aplikáciu"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikácia alebo vaša organizácia túto akciu nepovoľuje"</string> <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Chcete preskočiť návod na navigáciu?"</string> @@ -103,7 +97,7 @@ <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Preskočiť"</string> <string name="accessibility_rotate_button" msgid="4771825231336502943">"Otočiť obrazovku"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Panel vzdelávacích aplikácií"</string> - <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Ak chcete použiť dve aplikácie naraz, presuňte aplikáciu nabok"</string> + <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Ak chcete použ. dve aplikácie naraz, presuňte aplik. nabok"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Panel aplikácií zobrazíte pomalým potiahnutím nahor"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Získavajte návrhy aplikácií na základe svojich zvykov"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Ak chcete, aby sa panel aplikácií autom. skrýval, zapnite v Nastaveniach navigáciu gestami"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Panel aplikácií je zobrazený"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Panel aplikácií je skrytý"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigačný panel"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Zobrazovať panel aplikácií"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Zmeniť režim navigácie"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Rozdeľovač panela aplikácií"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Presunúť hore alebo doľava"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Presunúť dole alebo doprava"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Zobraziť # ďalšiu aplikáciu.}few{Zobraziť # ďalšie aplikácie.}many{Show # more apps.}other{Zobraziť # ďalších aplikácií.}}"</string> diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml index e72dac1993..e0f1f92b9e 100644 --- a/quickstep/res/values-sl/strings.xml +++ b/quickstep/res/values-sl/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Predlogi aplikacij so omogočeni."</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Predlogi aplikacij so onemogočeni."</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predvidena aplikacija: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Zasukajte napravo"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Zasukajte napravo, če si želite ogledati vadnico za krmarjenje s potezami"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Pazite, da povlečete s skrajno desnega ali skrajno levega roba."</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Pazite, da povlečete z desnega ali levega roba do sredine zaslona in dvignete prst."</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Pazite, da povlečete s skrajno desnega ali skrajno levega roba."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Pazite, da povlečete z desnega ali levega roba do sredine zaslona in dvignete prst."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Naučili ste se, kako povlečete z desne za vrnitev. Zdaj se naučite preklapljanja med aplikacijami."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Izvedli ste potezo za pomik nazaj."</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Pazite, da ne povlečete preblizu dna zaslona."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Izvedli ste potezo za pomik nazaj."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Pazite, da ne povlečete preblizu dna zaslona."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Občutljivost poteze za nazaj lahko spremenite v nastavitvah."</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Povlecite za vrnitev"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Če se želite vrniti na prejšnji zaslon, povlecite z levega ali desnega roba do sredine zaslona."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Če se želite vrniti na zadnji prikazani zaslon, z dvema prstoma povlecite z levega ali desnega roba do sredine zaslona."</string> - <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Pomik nazaj"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Povlecite z levega ali desnega roba do sredine zaslona."</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Pazite, da povlečete s spodnjega roba zaslona navzgor."</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Pazite, da ne zaustavite prsta, preden ga dvignete."</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Pazite, da povlečete naravnost navzgor."</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Izvedli ste potezo za pomik na začetni zaslon. Zdaj se naučite, kako se pomaknete nazaj."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Izvedli ste potezo za pomik na začetni zaslon."</string> + <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Nazaj"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Pazite, da povlečete s spodnjega roba zaslona navzgor."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pazite, da ne zaustavite prsta, preden ga dvignete."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Pazite, da povlečete naravnost navzgor."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Izvedli ste potezo za pomik na začetni zaslon. Zdaj se naučite, kako se pomaknete nazaj."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Izvedli ste potezo za pomik na začetni zaslon."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Povlecite za pomik na začetni zaslon"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Z dna zaslona s prstom povlecite navzgor. S to potezo lahko vedno odprete začetni zaslon."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Z dvema prstoma povlecite navzgor z dna zaslona. S to potezo lahko vedno odprete začetni zaslon."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Pomik na začetni zaslon"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Z dna zaslona s prstom povlecite navzgor."</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Odlično!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Pazite, da povlečete s spodnjega roba zaslona navzgor."</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Poskusite okno pridržati dalj časa, preden ga izpustite."</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Pazite, da povlečete naravnost navzgor in nato zaustavite prst."</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Za pomik na začetni zaslon lahko kadar koli povlečete navzgor z dna zaslona."</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Pazite, da povlečete s spodnjega roba zaslona navzgor."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Poskusite okno pridržati dalj časa, preden ga izpustite."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Pazite, da povlečete naravnost navzgor in nato zaustavite prst."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Naučili ste se uporabljati poteze. Poteze lahko izklopite v nastavitvah."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Izvedli ste potezo za preklapljanje med aplikacijami."</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Izvedli ste potezo za preklapljanje med aplikacijami."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Povlecite za preklapljanje med aplikacijami"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Za preklapljanje med aplikacijami povlecite navzgor z dna zaslona, pridržite in nato izpustite."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Za preklop med aplikacijami z dvema prstoma povlecite navzgor z dna zaslona, pridržite in spustite."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Preklop aplikacij"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Povlecite navzgor z dna zaslona, pridržite, nato izpustite."</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Odlično!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Zdaj znate"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Končano"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Nastavitve"</string> @@ -104,7 +98,7 @@ <string name="accessibility_rotate_button" msgid="4771825231336502943">"Sukanje zaslona"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Poučni nasveti o opravilni vrstici"</string> <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Povlecite aplikacijo na stran za uporabo 2 aplikacij hkrati."</string> - <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Počasi povlecite navzgor za prikaz opravilne vrstice"</string> + <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Počasi povlecite navzgor za prikaz opravilne vrstice."</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Prejemajte predloge aplikacij na podlagi svojih navad."</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"V nastavitvah vklopite krmarjenje s potezami, da se bo opravilna vrstica samodejno skrila."</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Naredite več z opravilno vrstico"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Opravilna vrstica je prikazana"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Opravilna vrstica je skrita"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Vrstica za krmarjenje"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Stalen prikaz opravilne vrstice"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Spreminjanje načina navigacije"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Razdelilnik opravilne vrstice"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premakni na vrh/levo"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premakni na dno/desno"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Pokaži še # aplikacijo.}one{Pokaži še # aplikacijo.}two{Pokaži še # aplikaciji.}few{Pokaži še # aplikacije.}other{Pokaži še # aplikacij.}}"</string> diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml index 6f661c3d0b..c0d550be35 100644 --- a/quickstep/res/values-sq/strings.xml +++ b/quickstep/res/values-sq/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Aplikacionet e sugjeruara janë aktivizuar"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sugjerimet e aplikacioneve janë çaktivizuar"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplikacioni i parashikuar: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rrotullo pajisjen"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rrotullo pajisjen për të përfunduar udhëzuesin e navigimit me gjeste"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Sigurohu që të rrëshqasësh shpejt nga skaji më i djathtë ose më i majtë"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Sigurohu që të rrëshqasësh shpejt nga skaji i djathtë ose i majtë drejt mesit të ekranit dhe lëshoje"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Sigurohu që të rrëshqasësh shpejt nga skaji më i djathtë ose më i majtë."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Sigurohu që të rrëshqasësh shpejt nga skaji i djathtë ose i majtë drejt mesit të ekranit dhe lëshoje."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Ke mësuar si të rrëshqasësh shpejt nga e djathta për t\'u kthyer prapa. Në vijim do të mësosh se si t\'i ndërrosh aplikacionet."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"E ke përfunduar gjestin e kthimit prapa"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Sigurohu që të mos rrëshqasësh shpejt shumë afër pjesës së poshtme të ekranit"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"E ke përfunduar gjestin e kthimit prapa."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Sigurohu që të mos rrëshqasësh shumë afër fundit të ekranit."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Për të ndryshuar ndjeshmërinë e gjestit të kthimit prapa, shko te \"Cilësimet\""</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Rrëshqit shpejt për t\'u kthyer prapa"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Për t\'u kthyer prapa tek ekrani i fundit, rrëshqit shpejt nga skaji i majtë ose i djathtë drejt mesit të ekranit"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Për t\'u kthyer prapa tek ekrani i fundit, rrëshqit shpejt me 2 gishta nga skaji i majtë ose i djathtë drejt mesit të ekranit."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Kthehu prapa"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Rrëshqit shpejt nga skaji i majtë ose i djathtë drejt mesit të ekranit"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Sigurohu që të rrëshqasësh shpejt lart nga skaji i poshtëm i ekranit"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Sigurohu që të mos ndalosh para se ta lëshosh"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Sigurohu që të rrëshqasësh shpejt drejt lart"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"E ke përfunduar gjestin e kalimit tek ekrani bazë. Në vijim, mëso si të kthehesh prapa."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"E ke përfunduar gjestin e kalimit tek ekrani bazë"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Sigurohu që të rrëshqasësh shpejt lart nga skaji i poshtëm i ekranit."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Sigurohu që të mos ndalosh para se ta lëshosh."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Sigurohu që të rrëshqasësh shpejt drejt lart."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"E ke përfunduar gjestin e kalimit tek ekrani bazë. Në vijim do të mësosh si të kthehesh prapa."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"E ke përfunduar gjestin e kalimit tek ekrani bazë."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Rrëshqit shpejt për të kaluar tek ekrani bazë"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Rrëshqit shpejt lart nga fundi i ekranit tënd. Ky gjest të dërgon gjithmonë tek ekrani bazë."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Rrëshqit shpejt lart me 2 gishta nga fundi i ekranit. Ky gjest të dërgon gjithmonë tek ekrani bazë."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Shko tek ekrani bazë"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Rrëshqit shpejt lart nga pjesa e poshtme e ekranit"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Punë e shkëlqyer!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Sigurohu që të rrëshqasësh shpejt lart nga skaji i poshtëm i ekranit"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Provo ta mbash shtypur dritaren për një kohë më të gjatë para se ta lëshosh"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Sigurohu që të rrëshqasësh shpejt drejt lart dhe më pas ndalo"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Për të shkuar tek ekrani bazë në çdo kohë, rrëshqit shpejt lart nga fundi i ekranit"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Sigurohu që të rrëshqasësh shpejt lart nga skaji i poshtëm i ekranit."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Provo ta mbash shtypur dritaren për një kohë më të gjatë para se ta lëshosh."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Sigurohu që të rrëshqasësh shpejt drejt lart dhe më pas ndalo."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Ke mësuar si të përdorësh gjestet. Për t\'i çaktivizuar gjestet, shko te \"Cilësimet\"."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"E ke përfunduar gjestin e ndërrimit të aplikacioneve"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"E ke përfunduar gjestin e ndërrimit të aplikacioneve."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Rrëshqit shpejt për të ndërruar aplikacionet"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Për të ndërruar mes aplikacioneve, rrëshqit shpejt lart nga fundi i ekranit tënd, mbaj dhe pastaj lësho."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Për të ndërruar mes aplikacioneve, rrëshqit lart me 2 gishta nga fundi i ekranit, mbaje dhe lëshoje."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Ndërro aplikacionet"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Rrëshqit shpejt lart nga fundi i ekranit, mbaje të shtypur dhe më pas lëshoje"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Shumë mirë!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Plotësisht gati"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"U krye"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Cilësimet"</string> @@ -88,7 +82,7 @@ <string name="allset_title" msgid="5021126669778966707">"Plotësisht gati!"</string> <string name="allset_hint" msgid="459504134589971527">"Rrëshqit shpejt lart për të shkuar në ekranin bazë"</string> <string name="allset_button_hint" msgid="2395219947744706291">"Trokit te butoni \"kreu\" për të shkuar tek ekrani bazë"</string> - <string name="allset_description_generic" msgid="5385500062202019855">"Je gati që të fillosh ta përdorësh këtë <xliff:g id="DEVICE">%1$s</xliff:g>"</string> + <string name="allset_description_generic" msgid="5385500062202019855">"Je gati që të fillosh të përdorësh <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="default_device_name" msgid="6660656727127422487">"pajisje"</string> <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Cilësimet e navigimit të sistemit"</annotation></string> <string name="action_share" msgid="2648470652637092375">"Ndaj"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Shiriti i detyrave i shfaqur"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Shiriti i detyrave i fshehur"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Shiriti i navigimit"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Shfaq gjithmonë shiritin e detyrave"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Ndrysho modalitetin e navigimit"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Ndarësi i shiritit të detyrave"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Lëviz në krye/majtas"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Lëviz në fund/djathtas"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Shfaq # aplikacion tjetër.}other{Shfaq # aplikacione të tjera.}}"</string> diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml index c985e08a86..17802836bc 100644 --- a/quickstep/res/values-sr/strings.xml +++ b/quickstep/res/values-sr/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Предлози апликација су омогућени"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Предлози апликација су онемогућени"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Предвиђамо апликацију: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Ротирајте уређај"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Ротирајте уређај да бисте довршили водич за навигацију помоћу покрета"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Обавезно превуците од саме десне или леве ивице"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Обавезно превуците од десне или леве ивице до средине екрана и отпустите"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Обавезно превуците од саме десне или леве ивице."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Обавезно превуците од десне или леве ивице до средине екрана и отпустите."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Научили сте како да превлачите здесна да бисте се вратили уназад. Сада научите да замените апликације."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Довршили сте покрет за повратак"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Никако не превлачите превише близу дна екрана"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Довршили сте покрет за повратак."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Никако не превлачите превише близу дна екрана."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Осетљивост пок. за назад можете да промените у Подешавањима"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Превуците да бисте се вратили уназад"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Да бисте се вратили на последњи екран, превуците од леве или десне ивице до средине екрана."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Да бисте се вратили на последњи екран, превуците помоћу два прста од леве или десне ивице до средине екрана."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Назад"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Превуците од леве или десне ивице до средине екрана"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Обавезно превуците нагоре од доње ивице екрана"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Никако не стајте пре отпуштања"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Обавезно превуците право нагоре"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Довршили сте покрет за повратак на почетну страницу. Сада сазнајте како да се вратите."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Довршили сте покрет за повратак на почетну страницу."</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Обавезно превуците нагоре од доње ивице екрана."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Никако не стајте пре отпуштања."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Обавезно превуците право нагоре."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Довршили сте покрет за повратак на почетну страницу. Сада сазнајте како да се вратите."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Довршили сте покрет за повратак на почетну страницу."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Превуците да бисте отишли на почетну страницу"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Превуците нагоре од дна екрана. Овај покрет вас увек води на почетни екран."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Превуците помоћу два прста нагоре од дна екрана. Овим покретом увек отварате почетни екран."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Идите на почетни екран"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Превуците нагоре са доњег дела екрана"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Одлично!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Обавезно превуците нагоре од доње ивице екрана"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Пробајте да држите прозор дуже пре отпуштања"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Обавезно превуците право нагоре, па застаните"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Да бисте отишли на почетни екран у било ком тренутку, превуците нагоре од дна екрана."</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Обавезно превуците нагоре од доње ивице екрана."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Пробајте да држите прозор дуже пре отпуштања."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Обавезно превуците право нагоре, па застаните."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Научили сте како да користите покрете. Да бисте искључили покрете, идите на подешавања."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Довршили сте покрет за промену апликација"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Довршили сте покрет за промену апликација."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Превуците да бисте заменили апликације"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"За прелазак са једне апликације на другу превуците нагоре од дна екрана, задржите, па пустите."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"За прелазак између апликација превуците помоћу два прста нагоре од дна екрана, задржите, па пустите."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Пређите на другу апликацију"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Превуците нагоре од дна екрана, задржите, па пустите"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Одлично!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"То је то"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Готово"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Подешавања"</string> @@ -104,7 +98,7 @@ <string name="accessibility_rotate_button" msgid="4771825231336502943">"Ротирајте екран"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Упутства на траци задатака"</string> <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Превуците на страну да бисте користили 2 апликације одједном"</string> - <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Споро превуците нагоре да бисте приказали траку задатака"</string> + <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Накратко превуците нагоре да бисте приказали траку задатака"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Добијајте предлоге апликација на основу рутине"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Укључите навигацију помоћу покрета у Подешавањима ради аутоматског скривања траке задатака"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Урадите више помоћу траке задатака"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Трака задатака је приказана"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Трака задатака је скривена"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Трака за навигацију"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Увек приказуј траку задатака"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Промени режим навигације"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Разделник траке задатака"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Премести горе лево"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Премести доле десно"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Прикажи још # апликацију.}one{Прикажи још # апликацију.}few{Прикажи још # апликације.}other{Прикажи још # апликација.}}"</string> diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml index 7e97556675..5e8467514d 100644 --- a/quickstep/res/values-sv/strings.xml +++ b/quickstep/res/values-sv/strings.xml @@ -21,7 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="recent_task_option_pin" msgid="7929860679018978258">"Fäst"</string> <string name="recent_task_option_freeform" msgid="48863056265284071">"Fritt format"</string> - <string name="recents_empty_message" msgid="7040467240571714191">"Listan är tom"</string> + <string name="recents_empty_message" msgid="7040467240571714191">"Listan med de senaste åtgärderna är tom"</string> <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Inställningar för appanvändning"</string> <string name="recents_clear_all" msgid="5328176793634888831">"Rensa alla"</string> <string name="accessibility_recent_apps" msgid="4058661986695117371">"Senaste apparna"</string> @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Appförslag har aktiverats"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Appförslag har inaktiverats"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Appförslag: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotera enheten"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rotera enheten för att slutföra guiden för navigering med rörelser"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Se till att du sveper ända från högerkanten eller vänsterkanten"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Se till att du sveper från den högra eller vänstra kanten till mitten av skärmen och sedan släpper"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Se till att du sveper ända från högerkanten eller vänsterkanten."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Se till att du sveper från den högra eller vänstra kanten till mitten av skärmen och sedan släpper."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Nu kan du svepa från höger för att gå tillbaka. Nu ska du få lära dig hur du byter mellan appar."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Du är klar med rörelsen för att gå tillbaka"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Se till att du inte sveper för nära skärmens nederkant"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Du är klar med rörelsen för att gå tillbaka."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Se till att du inte sveper för nära skärmens nederkant."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Öppna inställningarna om du vill ändra rörelsens känslighet"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Svep för att återgå"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Återgå till den senaste skärmen genom att svepa från skärmens vänster- eller högerkant till mitten."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Gå tillbaka till den senaste skärmen genom att med två fingrar svepa mot mitten av skärmen från vänster eller höger kant."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Tillbaka"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Svep från den högra eller vänstra kanten till mitten av skärmen"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Se till att du sveper uppåt från nederkanten av skärmen"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Se till att du sveper i en jämn rörelse innan du släpper"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Se till att du sveper rakt uppåt"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Du är klar med rörelsen för att öppna startskärmen. Nu ska du få lära dig hur du går tillbaka."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Du är klar med rörelsen för att öppna startskärmen"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Se till att du sveper från nederkanten på skärmen."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Se till att du sveper i en jämn rörelse innan du släpper."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Se till att du sveper rakt uppåt."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Du är klar med rörelsen för att öppna startskärmen. Nu ska du få lära dig hur du går tillbaka."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Du är klar med rörelsen för att öppna startskärmen."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Svep för att öppna startskärmen"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Svep uppåt från skärmens nederkant. Du kan alltid återgå till startskärmen med den här rörelsen."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Svep uppåt med två fingrar från skärmens nederkant. Så kommer du alltid tillbaka till startskärmen."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Öppna startskärmen"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Svep uppåt från skärmens nederkant"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Bra jobbat!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Se till att du sveper uppåt från nederkanten av skärmen"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Testa att trycka längre på fönstret innan du släpper"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Se till att du sveper rakt uppåt och sedan pausar"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Öppna startskärmen när som helst genom att svepa uppåt från skärmens nederkant"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Se till att du sveper från nederkanten på skärmen."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Testa att trycka längre på fönstret innan du släpper."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Se till att du sveper rakt uppåt och sedan pausar."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Du har lärt dig hur du använder rörelser. Om du vill inaktivera rörelser öppnar du inställningarna."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Du är klar med rörelsen för att byta mellan appar"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Du är klar med rörelsen för att byta mellan appar."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Svep för att byta mellan appar"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Byt mellan appar genom att svepa uppåt från skärmens nederkant. Håll fingret nedtryckt och släpp."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Byta mellan appar: Svep uppåt med två fingrar från skärmens nederkant, håll kvar och släpp sedan."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Byt app"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Svep uppåt från skärmens nederkant. Håll fingret nedtryckt och släpp sedan"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Bra gjort!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Klart"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Klar"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Inställningar"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Aktivitetsfältet visas"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Aktivitetsfältet är dolt"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigeringsfält"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Visa alltid aktivitetsfältet"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Ändra navigeringsläge"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Avdelare för aktivitetsfältet"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytta högst upp/till vänster"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytta längst ned/till höger"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Visa # app till.}other{Visa # appar till.}}"</string> diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml index 983964b01e..bc7cfb8269 100644 --- a/quickstep/res/values-sw/strings.xml +++ b/quickstep/res/values-sw/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Mapendekezo ya programu yamewashwa"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Umezima mapendekezo ya programu"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Programu iliyotabiriwa: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Zungusha kifaa chako"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Tafadhali zungusha kifaa chako ili ukamilishe mafunzo ya usogezaji kwa kutumia ishara"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Hakikisha unatelezesha kidole kutoka ukingo wa kulia au kushoto kabisa"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Hakikisha unatelezesha kidole kutoka ukingo wa kulia au kushoto hadi katikati ya skrini na uachilie"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Hakikisha unatelezesha kidole kuanzia ukingo wa kulia kabisa au ukingo wa kushoto kabisa."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Hakikisha unatelezesha kidole kuanzia ukingo wa kulia au kushoto kuelekea katikati ya skrini na uachilie."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Umejifunza jinsi ya kutelezesha kidole kuanzia kulia ili kurudi nyuma. Sasa jifunze jinsi ya kubadilisha programu."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Umeweka ishara ya kurudi nyuma"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Hakikisha hutelezeshi kidole karibu sana na sehemu ya chini ya skrini"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Umekamilisha ishara ya kurudi nyuma."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Hakikisha hutelezeshi kidole karibu sana na sehemu ya chini ya skrini."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Kubadilisha hisi ya ishara ya nyuma, nenda kwenye Mipangilio"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Telezesha kidole ili urudi nyuma"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Ili urudi kwenye skrini iliyotangulia, telezesha kidole kuanzia ukingo wa kushoto au wa kulia kuelekea katikati ya skrini."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Ili urudi kwenye skrini iliyopita, telezesha vidole viwili kuanzia ukingo wa kushoto au wa kulia kuelekea katikati ya skrini."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Rudi nyuma"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Telezesha kidole kutoka ukingo wa kushoto au kulia hadi katikati ya skrini"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Hakikisha unatelezesha kidole juu kuanzia ukingo wa chini wa skrini"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Hakikisha husitishi kabla ya kuachilia"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Hakikisha unatelezesha kidole juu"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Umeweka ishara ya kwenda kwenye Skrini ya kwanza. Inayofuata, jifunze jinsi ya kurudi nyuma."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Umeweka ishara ya kwenda kwenye skrini ya kwanza"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Hakikisha unatelezesha kidole juu kuanzia ukingo wa chini wa skrini."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Hakikisha kuwa husimamishi kabla ya kuachilia."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Hakikisha unatelezesha kidole kuelekea juu."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Umekamilisha ishara ya kwenda kwenye Skrini ya kwanza. Sasa jifunze jinsi ya kurudi nyuma."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Umekamilisha ishara ya kwenda kwenye Skrini ya kwanza."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Telezesha kidole ili uende kwenye skrini ya kwanza"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Telezesha kidole juu kuanzia chini ya skrini yako. Ishara hii kila wakati hukupeleka kwenye Skrini ya kwanza."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Telezesha vidole viwili kuelekea juu kuanzia sehemu ya chini ya skrini. Ishara hii kila wakati hukupeleka kwenye Skrini ya kwanza."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Nenda kwenye ukurasa wa mwanzo"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Telezesha kidole juu kutoka sehemu ya chini ya skrini yako"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Kazi nzuri!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Hakikisha unatelezesha kidole juu kuanzia ukingo wa chini wa skrini"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Jaribu kushikilia dirisha kwa muda mrefu kabla ya kuachilia"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Hakikisha unatelezesha kidole juu, kisha usitishe"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Ili uende kwenye skrini ya kwanza muda wowote, telezesha kidole juu kutoka sehemu ya chini ya skrini"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Hakikisha unatelezesha kidole juu kuanzia ukingo wa chini wa skrini."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Jaribu kushikilia dirisha kwa muda mrefu kabla ya kuachilia."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Hakikisha unatelezesha kidole kuelekea juu, kisha usimamishe."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Umejifunza jinsi ya kutumia ishara. Ili uzime ishara, nenda kwenye Mipangilio."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Umeweka ishara ya kubadilisha programu"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Umekamilisha ishara ya kubadilisha programu."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Telezesha kidole ili ubadilishe programu"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Ili ubadili kati ya programu, telezesha kidole juu kuanzia sehemu ya chini ya skrini yako, ushikilie, kisha uachilie."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Ili ubadilishe kati ya programu, telezesha vidole viwili kuelekea juu kuanzia sehemu ya chini ya skrini yako, ushikilie, kisha uachilie."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Badilisha programu"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Telezesha kidole juu kutoka sehemu ya chini ya skrini yako, shikilia kisha uachilie"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Hongera!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Kila kitu kiko tayari"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Nimemaliza"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Mipangilio"</string> @@ -88,7 +82,7 @@ <string name="allset_title" msgid="5021126669778966707">"Tayari!"</string> <string name="allset_hint" msgid="459504134589971527">"Telezesha kidole juu ili uende kwenye skrini ya kwanza"</string> <string name="allset_button_hint" msgid="2395219947744706291">"Gusa kitufe cha ukurasa wa mwanzo ili uende kwenye skrini ya kwanza"</string> - <string name="allset_description_generic" msgid="5385500062202019855">"Uko tayari kuanza kutumia <xliff:g id="DEVICE">%1$s</xliff:g>"</string> + <string name="allset_description_generic" msgid="5385500062202019855">"Uko tayari kuanza kutumia <xliff:g id="DEVICE">%1$s</xliff:g> yako"</string> <string name="default_device_name" msgid="6660656727127422487">"kifaa"</string> <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Mipangilio ya usogezaji kwenye mfumo"</annotation></string> <string name="action_share" msgid="2648470652637092375">"Shiriki"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Upauzana umeonyeshwa"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Upauzana umefichwa"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Sehemu ya viungo muhimu"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Onyesha Upauzana kila wakati"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Badilisha hali ya usogezaji"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Kitenganishi cha Upauzana"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sogeza juu/kushoto"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sogeza chini/kulia"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Onyesha programu # zaidi.}other{Onyesha programu # zaidi.}}"</string> diff --git a/quickstep/res/values-sw720dp/dimens.xml b/quickstep/res/values-sw720dp/dimens.xml index 9e832bc5e4..1caffb8a72 100644 --- a/quickstep/res/values-sw720dp/dimens.xml +++ b/quickstep/res/values-sw720dp/dimens.xml @@ -43,4 +43,7 @@ <dimen name="taskbar_app_window_threshold">100dp</dimen> <dimen name="taskbar_home_overview_threshold">180dp</dimen> <dimen name="taskbar_catch_up_threshold">300dp</dimen> + + <!-- Taskbar swipe up threshold multipliers --> + <item name="taskbar_nav_threshold_mult" format="float" type="dimen">3</item> </resources> diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml index 2016c9f70e..5ccbc17767 100644 --- a/quickstep/res/values-ta/strings.xml +++ b/quickstep/res/values-ta/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ஆப்ஸ் பரிந்துரைகள் இயக்கப்பட்டுள்ளன"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ஆப்ஸ் பரிந்துரைகள் முடக்கப்பட்டுள்ளன"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"கணித்த ஆப்ஸ்: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"உங்கள் சாதனத்தைச் சுழற்றுங்கள்"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"சைகை வழிசெலுத்தல் பயிற்சியை நிறைவுசெய்ய உங்கள் சாதனத்தைச் சுழற்றுங்கள்"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"வலது அல்லது இடது ஓரத்தின் விளிம்பிலிருந்து ஸ்வைப் செய்வதை உறுதிசெய்துகொள்ளுங்கள்"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"வலது அல்லது இடது ஓரத்திலிருந்து திரையின் மையப் பகுதிக்கு ஸ்வைப் செய்தபிறகு விடுவிப்பதை உறுதிசெய்க"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"வலது அல்லது இடது ஓரத்தின் விளிம்பிலிருந்து ஸ்வைப் செய்வதை உறுதிசெய்க."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"வலது அல்லது இடது ஓரத்திலிருந்து திரையின் மையப் பகுதிக்கு ஸ்வைப் செய்தபிறகு விடுவிப்பதை உறுதிசெய்க."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"பின்செல்வதற்கு எப்படி வலதுபுறத்திலிருந்து ஸ்வைப் செய்வதென்று கற்றுக்கொண்டீர்கள். அடுத்து ஆப்ஸுக்கிடையே எப்படி மாறுவது என்பதை அறிக."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"பின்செல் சைகைப் பயிற்சியை நிறைவுசெய்துவிட்டீர்கள்"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"திரையின் கீழ்ப்பகுதிக்கு மிக நெருக்கமாக ஸ்வைப் செய்யவில்லை என்பதை உறுதிசெய்துகொள்ளுங்கள்"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"பின்செல் சைகைப் பயிற்சியை முடித்துவிட்டீர்கள்."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"திரையின் கீழ்பகுதிக்கு மிக நெருக்கமாக ஸ்வைப் செய்யவில்லை என்பதை உறுதிசெய்துகொள்ளுங்கள்."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"பின்செல் சைகையின் உணர்திறனை மாற்ற அமைப்புகளுக்குச் செல்க"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"பின்செல்ல ஸ்வைப் செய்யுங்கள்"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"முந்தைய திரைக்கு மீண்டும் செல்ல, இடது/வலது ஓரத்திலிருந்து திரையின் மையப் பகுதிக்கு ஸ்வைப் செய்க."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"முந்தைய திரைக்கு மீண்டும் செல்ல, 2 விரல்களால் இடது அல்லது வலது ஓரத்திலிருந்து திரையின் மையப் பகுதிக்கு ஸ்வைப் செய்யுங்கள்."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"பின்செல்லுதல்"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"வலது அல்லது இடது ஓரத்திலிருந்து திரையின் மையப் பகுதிக்கு ஸ்வைப் செய்யுங்கள்"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"திரையின் கீழ் ஓரத்திலிருந்து மேல்நோக்கி ஸ்வைப் செய்வதை உறுதிசெய்துகொள்ளுங்கள்"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"விடுவிப்பதற்கு முன்பாக இடைநிறுத்தவில்லை என்பதை உறுதிசெய்துகொள்ளுங்கள்"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"மேல்நோக்கி நேராக ஸ்வைப் செய்வதை உறுதிசெய்துகொள்ளுங்கள்"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"முகப்புக்குச் செல் சைகைப் பயிற்சியை நிறைவுசெய்துவிட்டீர்கள். அடுத்து பின்செல்வது எப்படி என்பதை அறிக."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"முகப்புக்குச் செல் சைகைப் பயிற்சியை நிறைவுசெய்துவிட்டீர்கள்"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"திரையின் கீழ் ஓரத்திலிருந்து மேல்நோக்கி ஸ்வைப் செய்வதை உறுதிசெய்துகொள்ளுங்கள்."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"விடுவிப்பதற்கு முன்பாக இடைநிறுத்தவில்லை என்பதை உறுதிசெய்துகொள்ளுங்கள்."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"மேல்நோக்கி நேராக ஸ்வைப் செய்வதை உறுதிசெய்துகொள்ளுங்கள்."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"முகப்புக்குச் செல் சைகைப் பயிற்சியை முடித்துவிட்டீர்கள். அடுத்து, பின்செல்வது எப்படி என்பதை அறிக."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"முகப்புக்குச் செல் சைகைப் பயிற்சியை முடித்துவிட்டீர்கள்."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"முகப்புக்குச் செல்ல ஸ்வைப் செய்யுங்கள்"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"திரையின் கீழிருந்து மேலாக ஸ்வைப் செய்க. இந்தச் சைகை எப்போதும் முகப்புத் திரைக்கு அழைத்துச் செல்லும்."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"2 விரலால் திரையின் கீழிருந்து மேலாக ஸ்வைப் செய்க. இந்தச் சைகை முகப்புத் திரைக்கு அழைத்துச் செல்லும்."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"முகப்புக்குச் செல்லுதல்"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"திரையின் கீழிருந்து மேல்நோக்கி ஸ்வைப் செய்யுங்கள்"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"அருமை!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"திரையின் கீழ் ஓரத்திலிருந்து மேல்நோக்கி ஸ்வைப் செய்வதை உறுதிசெய்துகொள்ளுங்கள்"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"விடுவிப்பதற்கு முன்பாக நீண்டநேரம் சாளரத்தை அழுத்திப் பிடித்திருங்கள்"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"மேல்நோக்கி நேராக ஸ்வைப் செய்தபிறகு இடைநிறுத்துவதை உறுதிசெய்துகொள்ளுங்கள்"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"எந்தத் திரையிலிருந்தும் முகப்புத் திரைக்குச் செல்ல திரையின் கீழிருந்து மேல்நோக்கி ஸ்வைப் செய்யுங்கள்"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"திரையின் கீழ் ஓரத்திலிருந்து மேல்நோக்கி ஸ்வைப் செய்வதை உறுதிசெய்துகொள்ளுங்கள்."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"விடுவிப்பதற்கு முன்பாக நீண்டநேரம் சாளரத்தை அழுத்திப் பிடித்திருங்கள்."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"மேல்நோக்கி நேராக ஸ்வைப் செய்தபிறகு இடைநிறுத்துவதை உறுதிசெய்துகொள்ளுங்கள்."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"சைகைகளை எப்படி உபயோகிப்பது என்று கற்றுக்கொண்டீர்கள். சைகைகளை முடக்க அமைப்புகளுக்குச் செல்லுங்கள்."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"ஆப்ஸுக்கிடையே மாறும் சைகைப் பயிற்சியை நிறைவுசெய்துவிட்டீர்கள்"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"ஆப்ஸுக்கிடையே மாறும் சைகைப் பயிற்சியை முடித்துவிட்டீர்கள்."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ஆப்ஸுக்கிடையே மாற ஸ்வைப் செய்யுங்கள்"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ஆப்ஸுக்கு இடையே மாற, திரையின் கீழிலிருந்து மேலாக ஸ்வைப் செய்து, பிடித்திருந்து, பிறகு விடுவிக்கவும்."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"ஆப்ஸுக்கிடையே மாற, திரையின் கீழிருந்து மேலாக 2 விரலால் ஸ்வைப் செய்து, பிடித்து, பிறகு விடுவிக்கவும்."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ஆப்ஸுக்கிடையே மாறுதல்"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"உங்கள் திரையின் கீழ்ப்பகுதியில் இருந்து மேலே ஸ்வைப் செய்து, பிடித்து, பிறகு விடுவியுங்கள்"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"அருமை!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"எல்லாம் தயார்"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"முடிந்தது"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"அமைப்புகள்"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"செயல் பட்டி காட்டப்படுகிறது"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"செயல் பட்டி மறைக்கப்பட்டுள்ளது"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"வழிசெலுத்தல் பட்டி"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"செயல் பட்டியை எப்போதும் காட்டு"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"வழிசெலுத்தல் பயன்முறையை மாற்று"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"செயல் பட்டிப் பிரிப்பான்"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"மேலே/இடதுபுறம் நகர்த்தும்"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"கீழே/வலதுபுறம் நகர்த்தும்"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{மேலும் # ஆப்ஸைக் காட்டு.}other{மேலும் # ஆப்ஸைக் காட்டு.}}"</string> diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml index b9d4b31ae7..ad0c851ca8 100644 --- a/quickstep/res/values-te/strings.xml +++ b/quickstep/res/values-te/strings.xml @@ -19,7 +19,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="recent_task_option_pin" msgid="7929860679018978258">"పిన్ చేయండి"</string> + <string name="recent_task_option_pin" msgid="7929860679018978258">"పిన్ చేయి"</string> <string name="recent_task_option_freeform" msgid="48863056265284071">"సంప్రదాయేతర"</string> <string name="recents_empty_message" msgid="7040467240571714191">"ఇటీవలి అంశాలు ఏవీ లేవు"</string> <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"యాప్ వినియోగ సెట్టింగ్లు"</string> @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"యాప్ సలహాలు ఎనేబుల్ చేయబడ్డాయి"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"యాప్ సూచనలు డిజేబుల్ చేయబడ్డాయి"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"సూచించబడిన యాప్: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"మీ పరికరాన్ని రొటేట్ చేయండి"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"సంజ్ఞ నావిగేషన్ ట్యుటోరియల్ను పూర్తి చేయడానికి దయచేసి మీ పరికరాన్ని రొటేట్ చేయండి"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"కుడి వైపు చిట్ట చివరి లేదా ఎడమ వైపు చిట్ట చివరి అంచు నుండి స్వైప్ చేస్తున్నారని నిర్ధారించుకోండి"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"మీరు కుడి లేదా ఎడమ అంచు నుండి స్క్రీన్ మధ్యలోకి స్వైప్ చేశారని నిర్ధారించుకుని, మీ వేలిని ఎత్తండి"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"కుడి వైపు చిట్ట చివరి లేదా ఎడమ వైపు చిట్ట చివరి అంచు నుండి స్వైప్ చేస్తున్నారని నిర్ధారించుకోండి."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"మీరు కుడి లేదా ఎడమ అంచు నుండి స్క్రీన్ మధ్యలోకి స్వైప్ చేశారని నిర్ధారించుకోని, మీ వేలిని ఎత్తండి."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"వెనుకకు వెళ్లడానికి కుడి నుండి స్వైప్ ఎలానో మీకు తెలుసు. తర్వాత, యాప్ల మధ్య ఎలా మారాలో తెలుసుకోండి."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"మీరు పేజీ నుండి వెనుకకు వెళ్లే సంజ్ఞను పూర్తి చేశారు"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"మీరు స్క్రీన్ దిగువకు చాలా దగ్గరగా స్వైప్ చేయకుండా చూసుకోండి"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"మీరు తిరిగి వెనక్కు వెళ్లే సంజ్ఞను పూర్తి చేశారు."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"మీరు స్క్రీన్ దిగువకు చాలా దగ్గరగా స్వైప్ చేయలేదని నిర్ధారించుకోండి."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"వెనుక సంజ్ఞ సున్నితత్వం మార్చడానికి, సెట్టింగ్లకు వెళ్లండి"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"వెనుకకు వెళ్ళడం కోసం స్వైప్ చేయండి"</string> - <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"మునుపటి స్క్రీన్కు తిరిగి వెళ్లడానికి, ఎడమ లేదా కుడి అంచు నుండి స్క్రీన్ మధ్యలోకి స్వయిప్ చేయండి."</string> + <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"మునుపటి స్క్రీన్కు తిరిగి వెళ్లడానికి, ఎడమ లేదా కుడి అంచు నుండి స్క్రీన్ మధ్యలోకి స్వైప్ చేయండి."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"గత స్క్రీన్కు తిరిగి వెళ్లడానికి, ఎడమ లేదా కుడి అంచు నుండి స్క్రీన్ మధ్యలోకి 2 వేళ్లతో స్వైప్ చేయండి."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"వెనుకకు వెళ్లండి"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"ఎడమ లేదా కుడి అంచు నుండి స్క్రీన్ మధ్యకు స్వైప్ చేయండి"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"మీరు స్క్రీన్ దిగువ అంచు నుండి పైకి స్వైప్ చేశారని నిర్ధారించుకోండి"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"మీరు స్క్రీన్పై మీ వేలిని ఎత్తే ముందు స్వైపింగ్ను ఆపకుండా చూసుకోండి"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"మీరు నేరుగా పైకి స్వైప్ చేశారని నిర్ధారించుకోండి"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"మీరు మొదటి స్క్రీన్కు వెళ్లే సంజ్ఞను పూర్తి చేశారు. తర్వాత, వెనుకకు ఎలా వెళ్లాలో తెలుసుకోండి."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"మీరు మొదటి స్క్రీన్కు వెళ్లే సంజ్ఞను పూర్తి చేశారు"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"మీరు స్క్రీన్ దిగువ అంచు నుండి పైకి స్వయిప్ చేస్తున్నారని నిర్ధారించుకోండి."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"బయలుదేరే ముందు మీరు పాజ్ చేయకుండా చూసుకోండి."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"మీరు నేరుగా పైకి స్వైప్ చేశారని నిర్ధారించుకోండి."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"మీరు మొదటి స్క్రీన్కు వెళ్లే సంజ్ఞను పూర్తి చేశారు. తర్వాత, వెనుకకు ఎలా వెళ్లాలో తెలుసుకోండి."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"మీరు మొదటి ట్యాబ్కు వెళ్లే సంజ్ఞను పూర్తి చేశారు."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"మొదటి స్క్రీన్కు వెళ్లడానికి స్వైప్ చేయండి"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"స్క్రీన్ కింది నుండి పైకి స్వైప్ చేయండి. ఈ సంజ్ఞ ఎప్పుడూ మిమ్మల్ని మొదటి స్క్రీన్కు తీసుకెళ్తుంది."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"స్క్రీన్ కింది నుండి 2 వేళ్లతో పైకి స్వైప్ చేయండి. సంజ్ఞ ఎల్లప్పుడూ మొదటి స్క్రీన్కు తీసుకెళ్తుంది."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"మొదటి ట్యాబ్కు వెళ్లండి"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"స్క్రీన్ కింది భాగం నుండి పైకి స్వైప్ చేయండి"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"బాగా చేశారు!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"మీరు స్క్రీన్ దిగువ అంచు నుండి పైకి స్వైప్ చేశారని నిర్ధారించుకోండి"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"వేలిని రిలీజ్ చేయడానికి ముందు విండోను ఎక్కువసేపు నొక్కి, పట్టుకోవడానికి ట్రై చేయండి"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"స్క్రీన్పై నేరుగా పైకి స్వైప్ చేసి, ఆపై పాజ్ చేయండి"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"ఎప్పుడైనా మీ మొదటి స్క్రీన్కు వెళ్లడానికి, మీ స్క్రీన్ దిగువ భాగం నుండి పైకి స్వైప్ చేయండి"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"మీరు స్క్రీన్ దిగువ అంచు నుండి పైకి స్వయిప్ చేస్తున్నారని నిర్ధారించుకోండి."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"రిలీజ్ చేయడానికి ముందు విండోను ఎక్కువసేపు పట్టుకోడానికి ట్రై చేయండి."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"మీరు నేరుగా స్వైప్ చేశారని నిర్ధారించుకోండి, ఆపై పాజ్ చేయండి."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"మీరు సంజ్ఞలను ఎలా ఉపయోగించాలో నేర్చుకున్నారు. సంజ్ఞలను ఆఫ్ చేయడానికి, సెట్టింగ్లకు వెళ్లండి."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"మీరు \'యాప్ల మధ్య మార్పు\' సంజ్ఞను పూర్తి చేశారు"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"మీరు \'యాప్ల మధ్య మార్పు\' సంజ్ఞను పూర్తి చేశారు."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"యాప్ల మధ్య మార్చడం కోసం స్వైప్ చేయండి"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"యాప్ల మధ్య మారడానికి, మీ స్క్రీన్ కింది వైపు నుండి పైకి స్వైప్ చేసి, పట్టుకుని, తర్వాత వదలండి."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"యాప్ల మధ్య మారడానికి, మీ స్క్రీన్ కింది నుండి 2 వేళ్లతో పైకి స్వైప్ చేసి, నొక్కి పట్టి, వదలండి."</string> - <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"యాప్ల మధ్య స్విచ్ అవ్వండి"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"మీ స్క్రీన్ కింది వైపు నుండి పైకి స్వైప్ చేసి, పట్టుకుని, తర్వాత వదలండి"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"చాలా బాగా చేశారు!"</string> + <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"యాప్ల మధ్య మారండి"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"అంతా సిద్ధంగా ఉంది"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"పూర్తయింది"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"సెట్టింగ్లు"</string> @@ -86,7 +80,7 @@ <string name="gesture_tutorial_nice" msgid="2936275692616928280">"పనితీరు బాగుంది!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"ట్యుటోరియల్ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"అంతా సెట్ అయింది!"</string> - <string name="allset_hint" msgid="459504134589971527">"వర్చువల్ హోమ్కు వెళ్లడానికి పైకి స్వైప్ చేయండి"</string> + <string name="allset_hint" msgid="459504134589971527">"హోమ్కు వెళ్లడానికి పైకి స్వైప్ చేయండి"</string> <string name="allset_button_hint" msgid="2395219947744706291">"మీ మొదటి స్క్రీన్కు వెళ్లడానికి హోమ్ బటన్ను ట్యాప్ చేయండి"</string> <string name="allset_description_generic" msgid="5385500062202019855">"మీరు ఇప్పుడు మీ <xliff:g id="DEVICE">%1$s</xliff:g>ను ఉపయోగించడం ప్రారంభించవచ్చు"</string> <string name="default_device_name" msgid="6660656727127422487">"పరికరం"</string> @@ -94,7 +88,7 @@ <string name="action_share" msgid="2648470652637092375">"షేర్ చేయండి"</string> <string name="action_screenshot" msgid="8171125848358142917">"స్క్రీన్షాట్"</string> <string name="action_split" msgid="2098009717623550676">"స్ప్లిట్ చేయండి"</string> - <string name="toast_split_select_app" msgid="8464310533320556058">"స్ప్లిట్ స్క్రీన్ కోసం మరొక యాప్ను ట్యాప్ చేయండి"</string> + <string name="toast_split_select_app" msgid="8464310533320556058">"మరొక యాప్ను ట్యాప్ చేసి, స్ప్లిట్ స్క్రీన్ వాడండి"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"స్ప్లిట్ స్క్రీన్ ఉపయోగానికి మరొక యాప్ ఎంచుకోండి"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ఈ చర్యను యాప్ గానీ, మీ సంస్థ గానీ అనుమతించవు"</string> <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"నావిగేషన్ ట్యుటోరియల్ను స్కిప్ చేయాలా?"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"టాస్క్బార్ చూపబడింది"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"టాస్క్బార్ దాచబడింది"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"నావిగేషన్ బార్"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"ఎప్పుడూ టాస్క్బార్ చూపించండి"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"నావిగేషన్ మోడ్ను మార్చండి"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"టాస్క్బార్ డివైడర్"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ఎగువ/ఎడమ వైపునకు తరలించండి"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"దిగువ/కుడి వైపునకు తరలించండి"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{మరో # యాప్ను చూడండి.}other{మరో # యాప్లను చూడండి.}}"</string> diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml index 0f921d1d23..8cba9b9117 100644 --- a/quickstep/res/values-th/strings.xml +++ b/quickstep/res/values-th/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"เปิดใช้แอปแนะนำแล้ว"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ปิดใช้คำแนะนำเกี่ยวกับแอปอยู่"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"แอปที่คาดว่าจะใช้: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"หมุนอุปกรณ์ของคุณ"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"โปรดหมุนอุปกรณ์เพื่อทำตามบทแนะนำการนำทางด้วยท่าทางสัมผัสให้เสร็จสมบูรณ์"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ตรวจสอบว่าปัดจากขอบด้านขวาสุดหรือซ้ายสุด"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"ตรวจสอบว่าปัดจากขอบด้านขวาหรือซ้ายไปตรงกลางหน้าจอ แล้วยกนิ้วขึ้น"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"ตรวจสอบว่าปัดจากขอบด้านขวาสุดหรือซ้ายสุด"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"ตรวจสอบว่าปัดจากขอบด้านขวาหรือซ้ายไปตรงกลางหน้าจอ แล้วยกนิ้วขึ้น"</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"คุณรู้วิธีปัดจากด้านขวาเพื่อย้อนกลับแล้ว ต่อไปดูวิธีสลับแอป"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับเสร็จแล้ว"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"ไม่ปัดใกล้กับด้านล่างของหน้าจอมากเกินไป"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับเสร็จแล้ว"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ตรวจสอบว่าไม่ได้ปัดใกล้กับด้านล่างของหน้าจอมากเกินไป"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"เปลี่ยนความไวของท่าทางสัมผัสเพื่อย้อนกลับได้ที่การตั้งค่า"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"ปัดเพื่อย้อนกลับ"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"หากต้องการย้อนกลับไปที่หน้าจอล่าสุด ให้ปัดจากขอบด้านซ้ายหรือขวาไปตรงกลางหน้าจอ"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"หากต้องการย้อนกลับไปที่หน้าจอล่าสุด ให้ใช้ 2 นิ้วปัดจากขอบด้านซ้ายหรือขวาไปตรงกลางหน้าจอ"</string> - <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"ย้อนกลับ"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"ปัดจากขอบด้านซ้ายหรือขวาไปตรงกลางหน้าจอ"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"ปัดขึ้นจากขอบด้านล่างของหน้าจอ"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"ไม่ต้องหยุดชั่วคราวก่อนยกนิ้วขึ้น"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"ปัดขึ้นในแนวตรง"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกเสร็จแล้ว ต่อไปดูวิธีย้อนกลับ"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกเสร็จแล้ว"</string> + <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"กลับ"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ปัดขึ้นจากขอบด้านล่างของหน้าจอ"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ตรวจสอบว่าไม่มีการหยุดชั่วคราวก่อนยกนิ้วขึ้น"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ตรวจสอบว่าปัดขึ้นในแนวตรง"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกเสร็จแล้ว ต่อไปดูวิธีย้อนกลับ"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกเสร็จแล้ว"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"ปัดเพื่อไปที่หน้าแรก"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ปัดขึ้นจากด้านล่างของหน้าจอ ท่าทางสัมผัสนี้จะนำคุณไปที่หน้าจอหลักเสมอ"</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"ใช้ 2 นิ้วปัดขึ้นจากด้านล่างของหน้าจอ ท่าทางสัมผัสนี้จะนำคุณไปที่หน้าจอหลักเสมอ"</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ไปที่หน้าจอหลัก"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"ปัดขึ้นจากด้านล่างของหน้าจอ"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"เก่งมาก"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"ปัดขึ้นจากขอบด้านล่างของหน้าจอ"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"ลองแตะหน้าต่างค้างไว้นานขึ้นก่อนปล่อยนิ้ว"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"ตรวจสอบว่าปัดขึ้นในแนวตรง แล้วหยุดชั่วคราว"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ไปที่หน้าแรก"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"ปัดขึ้นจากด้านล่างของหน้าจอเพื่อไปที่หน้าจอหลักได้ทุกเมื่อ"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ปัดขึ้นจากขอบด้านล่างของหน้าจอ"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"ลองแตะหน้าต่างค้างไว้นานขึ้นก่อนปล่อยนิ้ว"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ตรวจสอบว่าปัดขึ้นในแนวตรง แล้วหยุดชั่วคราว"</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"คุณรู้วิธีใช้ท่าทางสัมผัสแล้ว หากต้องการปิดท่าทางสัมผัส ให้ไปที่การตั้งค่า"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"คุณทำท่าทางสัมผัสเพื่อสลับแอปเสร็จแล้ว"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"คุณทำท่าทางสัมผัสเพื่อสลับแอปเสร็จแล้ว"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ปัดเพื่อสลับแอป"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"หากต้องการสลับระหว่างแอปต่างๆ ให้ปัดขึ้นจากด้านล่างของหน้าจอ ค้างไว้ แล้วปล่อย"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"หากต้องการสลับระหว่างแอป ให้ใช้ 2 นิ้วปัดขึ้นจากด้านล่างของหน้าจอค้างไว้แล้วปล่อย"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"เปลี่ยนแอป"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"ปัดขึ้นจากด้านล่างของหน้าจอ ค้างไว้ แล้วปล่อย"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"เยี่ยมมาก"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"เรียบร้อย"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"เสร็จสิ้น"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"การตั้งค่า"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"แถบงานแสดงอยู่"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"แถบงานซ่อนอยู่"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"แถบนำทาง"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"แสดงแถบงานเสมอ"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"เปลี่ยนโหมดการนําทาง"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ตัวแบ่งแถบงาน"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ย้ายไปที่ด้านบนหรือด้านซ้าย"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ย้ายไปที่ด้านล่างหรือด้านขวา"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{แสดงเพิ่มเติมอีก # แอป}other{แสดงเพิ่มเติมอีก # แอป}}"</string> diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml index 7185cb04cc..ae6dd45e40 100644 --- a/quickstep/res/values-tl/strings.xml +++ b/quickstep/res/values-tl/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Naka-enable ang mga iminumungkahing app"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Naka-disable ang mga iminumungkahing app"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Hinulaang app: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"I-rotate ang iyong device"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Paki-rotate ang iyong device para tapusin ang tutorial sa navigation gamit ang galaw"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Tiyaking magsa-swipe ka mula sa dulong kanan o dulong kaliwang gilid"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Tiyaking magsa-swipe mula sa kanan o kaliwang gilid papunta sa gitna ng screen at iangat ang daliri"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Tiyaking magsa-swipe ka mula sa dulong kanan o dulong kaliwang gilid."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Tiyaking mag-swipe mula sa kanan o kaliwang gilid papunta sa gitna ng screen at iangat ang daliri."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Natuto kang mag-swipe mula sa kanan para bumalik. Sunod, alamin kung paano magpalipat-lipat ng app."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Nakumpleto mo na ang galaw para bumalik"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Tiyaking hindi ka magsa-swipe nang masyadong malapit sa ibaba ng screen"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Nakumpleto mo na ang galaw para bumalik."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Tiyaking hindi ka magsa-swipe nang masyadong malapit sa ibaba ng screen."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Pumunta sa Settings para baguhin ang sensitivity ng pagbalik"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Mag-swipe para bumalik"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Para bumalik sa nakaraang screen, mag-swipe mula sa kaliwa o kanang gilid patungo sa gitna ng screen."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Para bumalik sa huling screen, mag-swipe gamit ang 2 daliri mula sa kaliwa o kanang gilid hanggang sa gitna ng screen."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Bumalik"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Mag-swipe mula sa kaliwa o kanang gilid papunta sa gitna ng screen"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Tiyaking magsa-swipe ka pataas mula sa pinakaibaba ng screen"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Tiyaking hindi ka magpo-pause bago iangat ang iyong daliri"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Tiyaking magsa-swipe ka nang diretso pataas"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Nakumpleto mo na ang galaw para pumunta sa home. Susunod, alamin kung paano bumalik."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Nakumpleto mo na ang galaw para pumunta sa home"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Tiyaking magsa-swipe ka pataas mula sa pinakaibaba ng screen."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Tiyaking hindi ka magpo-pause bago iangat ang iyong daliri."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Tiyaking magsa-swipe ka nang diretso pataas."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Nakumpleto mo na ang galaw para pumunta sa Home. Susunod, alamin kung paano bumalik."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Nakumpleto mo na ang galaw para pumunta sa Home."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Mag-swipe para pumunta sa home"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Mag-swipe pataas mula sa ibaba ng iyong screen. Dadalhin ka palagi ng galaw na ito sa Home screen."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Mag-swipe pataas gamit ang 2 daliri mula sa ibaba ng screen. Dadalhin ka palagi nito sa Home screen."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Pumunta sa home"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Mag-swipe pataas mula sa ibabang bahagi ng iyong screen"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Magaling!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Tiyaking magsa-swipe ka pataas mula sa pinakaibaba ng screen"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Subukang pindutin nang mas matagal ang window bago ito bitawan"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Tiyaking magsa-swipe ka nang diretso pataas, pagkatapos ay mag-pause"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Para pumunta sa iyong home screen anumang oras, mag-swipe pataas mula sa ibaba ng screen mo"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Tiyaking magsa-swipe ka pataas mula sa pinakaibaba ng screen."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Subukang pindutin nang mas matagal ang window bago ito bitawan."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Tiyaking magsa-swipe ka nang diretso pataas, pagkatapos ay mag-pause."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Alam mo na kung paano gumamit ng mga galaw. Para i-off ang mga galaw, pumunta sa Mga Setting."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Nakumpleto mo na ang galaw para magpalipat-lipat sa mga app"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Nakumpleto mo na ang galaw para magpalipat-lipat sa mga app."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Mag-swipe para lumipat ng app"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Para lumipat ng app, mag-swipe pataas mula sa ibaba ng iyong screen, mag-hold, at iangat ang daliri."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Para lumipat ng app, mag-swipe pataas gamit ang 2 daliri mula sa ibaba, mag-hold, at bumitaw."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Lumipat ng app"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Mag-swipe pataas mula sa ibaba ng iyong screen, i-hold ito saka bitawan"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Magaling!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Handa na ang lahat"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Tapos na"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Mga Setting"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Ipinapakita ang taskbar"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Nakatago ang taskbar"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Palaging ipakita ang Taskbar"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Magpalit ng navigation mode"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divider ng Taskbar"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Ilipat sa itaas/kaliwa"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Ilipat sa ibaba/kanan"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Magpakita ng # pang app.}one{Magpakita ng # pang app.}other{Magpakita ng # pang app.}}"</string> diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml index 1648b2228f..e5d5b6d68f 100644 --- a/quickstep/res/values-tr/strings.xml +++ b/quickstep/res/values-tr/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Uygulama önerileri etkinleştirildi"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Uygulama önerileri devre dışı bırakıldı"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Tahmin edilen uygulama: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Cihazınızı döndürün"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Hareketle gezinme eğitimini tamamlamak için lütfen cihazınızı döndürün"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"En sağ veya en sol kenardan kaydırdığınızdan emin olun"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Ekranın sağ veya sol kenarından ortasına doğru sürükleyip bıraktığınızdan emin olun."</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"En sağ veya en sol kenardan kaydırdığınızdan emin olun."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Ekranın sağ veya sol kenarından ortasına doğru sürükleyip bıraktığınızdan emin olun."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Geri dönmek için sağdan kaydırmayı öğrendiniz. Sırada uygulamalar arasında geçiş yapma var."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Geri dön hareketini tamamladınız"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Ekranın alt kısmına çok yakın bir şekilde kaydırmadığınızdan emin olun"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Geri dön hareketini tamamladınız."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Ekranın alt kısmına çok yakın bir şekilde kaydırmadığınızdan emin olun."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Geri hareketinin hassasiyetini değiştirmek için Ayarlar\'a gidin"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Geri dönmek için kaydırma"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Son ekrana geri gitmek için sol veya sağ kenardan ekranın ortasına doğru kaydırın."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Son ekrana geri gitmek için sol veya sağ kenardan ekranın ortasına doğru 2 parmağınızla kaydırın."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Geri dönme"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Sol veya sağ kenardan ekranın ortasına doğru kaydırın"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Ekranın alt kenarından yukarı kaydırdığınızdan emin olun"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Bırakmadan önce parmağınızı duraklatmadığınızdan emin olun"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Düz bir şekilde yukarı kaydırdığınızdan emin olun"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Ana ekrana git hareketini tamamladınız. Şimdi ana ekrana nasıl gideceğinizi öğreneceksiniz."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Ana ekrana git hareketini tamamladınız"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Ekranın alt kenarından yukarı kaydırdığınızdan emin olun."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Bırakmadan önce parmağınızı duraklatmadığınızdan emin olun."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Düz bir şekilde yukarı kaydırdığınızdan emin olun."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Ana ekrana git hareketini tamamladınız. Şimdi nasıl geri döneceğinizi öğreneceksiniz."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Ana ekrana git hareketini tamamladınız."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Ana ekrana gitmek için kaydırma"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Ekranın alt kısmından yukarıya doğru kaydırın. Bu hareket sizi her zaman Ana ekrana götürür."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Ekranın alt kısmından 2 parmağınızla yukarı kaydırın. Bu hareket sizi her zaman Ana ekrana götürür."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Ana sayfaya gitme"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Parmağınızı ekranın alt kısmından yukarıya doğru kaydırın"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Tebrikler!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Ekranın alt kenarından yukarı kaydırdığınızdan emin olun"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Bırakmadan önce pencereyi daha uzun süre tutmayı deneyin"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Düz bir şekilde yukarı kaydırıp ardından parmağınızı duraklattığınızdan emin olun"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Ana sayfaya gidin"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"İstediğiniz zaman ana ekrana gitmek için ekranınızın altından yukarı doğru kaydırın"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Ekranın alt kenarından yukarı kaydırdığınızdan emin olun."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Bırakmadan önce pencereyi daha uzun süre tutmayı deneyin."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Düz bir şekilde yukarı kaydırıp ardından parmağınızı duraklattığınızdan emin olun."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Hareketleri nasıl kullanacağınızı öğrendiniz. Hareketleri kapatmak için Ayarlar\'a gidin."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Uygulamalar arasında geçiş yapma hareketini tamamladınız"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Uygulamalar arasında geçiş yapma hareketini tamamladınız."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Uygulamalar arasında geçiş yapmak için kaydırma"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Uygulamalar arasında geçiş yapmak için ekranınızın altından yukarı kaydırıp basılı tutun ve sonra bırakın."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Uygulamalara geçiş yapmak için ekranın altından 2 parmakla yukarı kaydırıp basılı tutun ve bırakın."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Uygulamalar arasında geçiş yapma"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Ekranınızın alt tarafından yukarı doğru kaydırın, tutun ve sonra bırakın"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Tebrikler!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Hepsi bu kadar"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Bitti"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Ayarlar"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Görev çubuğu gösteriliyor"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Görev çubuğu gizlendi"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Gezinme çubuğu"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Görev çubuğunu daima göster"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Gezinme modunu değiştir"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Görev Çubuğu Ayırıcısı"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sol üste taşı"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sağ alta taşı"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# uygulama daha göster.}other{# uygulama daha göster}}"</string> diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml index 1f91165162..36c5bbb6cb 100644 --- a/quickstep/res/values-uk/strings.xml +++ b/quickstep/res/values-uk/strings.xml @@ -44,51 +44,45 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Рекомендовані додатки ввімкнено"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Рекомендовані додатки вимкнено"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Передбачений додаток: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Оберніть пристрій"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Обертайте пристрій, щоб ознайомитися з посібником із навігації за допомогою жестів"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Проведіть пальцем від самого краю екрана (правого або лівого)"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Проведіть пальцем від правого або лівого краю до середини екрана й підніміть палець"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Проведіть пальцем саме від правого або лівого краю екрана."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Проведіть пальцем від правого або лівого краю до середини екрана й підніміть палець."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Тепер ви знаєте, як повернутися на попередній екран, провівши пальцем справа наліво. Дізнайтеся, як переключатися між додатками."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Ви виконали жест \"Назад\""</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Не проводьте пальцем надто близько до нижнього краю екрана"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Ви виконали жест \"Назад\"."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Не проводьте пальцем надто близько до нижнього краю екрана."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Щоб змінити чутливість жесту \"Назад\", відкрийте налаштування"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Щоб повернутися, проведіть пальцем по екрану"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Щоб перейти на попередній екран, проведіть пальцем від лівого чи правого краю до середини екрана."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Щоб перейти на попередній екран, проведіть двома пальцями від лівого чи правого краю до середини екрана."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Повернення на попередній екран"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Проведіть пальцем від лівого чи правого краю до середини екрана"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Проведіть пальцем угору від нижнього краю екрана"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Не робіть паузу перед тим, як відірвати палець від екрана"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Проводьте пальцем вертикально вгору"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Ви виконали жест переходу на головний екран. Тепер дізнайтеся, як повернутися."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Ви виконали жест переходу на головний екран"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Проведіть пальцем угору від нижнього краю екрана."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Не робіть паузу перед тим, як відірвати палець від екрана."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Проводьте пальцем вертикально вгору."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Ви виконали жест переходу на головний екран. Тепер дізнайтеся, як повернутися."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Ви виконали жест переходу на головний екран."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Проведіть пальцем, щоб перейти на головний екран"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Проведіть пальцем по екрану знизу вгору. Цей жест завжди повертатиме вас на головний екран."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Проведіть двома пальцями вгору від низу екрана. Цей жест завжди спрямовує вас на головний екран."</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Перехід на головний екран"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Проведіть пальцем угору від низу екрана"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Чудово!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Проведіть пальцем угору від нижнього краю екрана"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Спробуйте втримувати вікно довше, перш ніж відпустити"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Проведіть пальцем вертикально вгору, а тоді зробіть паузу"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Перейти на головний екран"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Щоб будь-коли перейти на головний екран, проведіть пальцем вгору від низу екрана"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Проведіть пальцем угору від нижнього краю екрана."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Спробуйте втримувати вікно довше, перш ніж відпустити."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Проведіть пальцем вертикально вгору, а тоді зробіть паузу."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Ви вивчили жести. Щоб вимкнути їх, перейдіть у налаштування."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Ви виконали жест переходу в інший додаток"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Ви виконали жест переходу в інший додаток."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Проведіть пальцем, щоб перейти в інший додаток"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Щоб переключатися між додатками, проведіть знизу вгору по екрану, утримуйте палець, а потім відпустіть."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Щоб перейти в інший додаток, проведіть 2 пальцями від низу екрана, потримайте й відпустіть палець."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Перемикання між додатками"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Проведіть пальцем знизу вгору, утримуйте палець на екрані, а потім відпустіть"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Чудово!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Усе готово!"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Готово"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Налаштування"</string> <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Спробуйте ще"</string> <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Чудово!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"Навчальний посібник <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> - <string name="allset_title" msgid="5021126669778966707">"Готово!"</string> + <string name="allset_title" msgid="5021126669778966707">"Готово."</string> <string name="allset_hint" msgid="459504134589971527">"Щоб перейти на головний екран, проведіть пальцем угору"</string> <string name="allset_button_hint" msgid="2395219947744706291">"Натисніть кнопку головного екрана, щоб відкрити його"</string> - <string name="allset_description_generic" msgid="5385500062202019855">"Тепер ви можете використовувати <xliff:g id="DEVICE">%1$s</xliff:g>"</string> + <string name="allset_description_generic" msgid="5385500062202019855">"Тепер ви можете користуватися цим пристроєм: <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="default_device_name" msgid="6660656727127422487">"пристрій"</string> <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Системні налаштування навігації"</annotation></string> <string name="action_share" msgid="2648470652637092375">"Поділитися"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Панель завдань показано"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Панель завдань приховано"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Панель навігації"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Завжди показув. панель завдань"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Змінити режим навігації"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Розділювач панелі завдань"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перемістити вгору або вліво"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перемістити вниз або вправо"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Показати ще # додаток.}one{Показати ще # додаток.}few{Показати ще # додатки.}many{Показати ще # додатків.}other{Показати ще # додатка.}}"</string> diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml index 504f3aa31c..9d1efe49a9 100644 --- a/quickstep/res/values-ur/strings.xml +++ b/quickstep/res/values-ur/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ایپ کی تجاویز فعال ہیں"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ایپ کی تجاویز غیر فعال ہیں"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"پیشن گوئی کردہ ایپ: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"اپنا آلہ گھمائیں"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"براہ کرم اشاروں والی نیویگیشن کا ٹیوٹوریل مکمل کرنے کے لیے اپنا آلہ گھمائیں"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"یقینی بنائیں کہ آپ دائیں یا بائیں کنارے سے دور سے سوائپ کریں"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"یقینی بنائیں کہ آپ دائیں یا بائیں کنارے سے اسکرین کے وسط تک سوائپ کریں اور پھر اپنی انگلی اٹھا لیں"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"یقینی بنائیں کہ آپ دائیں یا بائیں کنارے سے دور سے سوئپ کریں۔"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"یقینی بنائیں کہ آپ دائیں یا بائیں کنارے سے اسکرین کے وسط تک سوائپ کریں اور پھر اپنی انگلی اٹھا لیں۔"</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"آپ نے واپس جانے کے لیے دائیں کنارے سے سوائپ کرنے کا طریقہ سیکھ لیا۔ اس کے بعد ایپس سوئچ کرنے کا طریقہ جانیں۔"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"آپ نے واپس جائیں اشارے کو مکمل کر لیا"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"اس بات کو یقینی بنائیں کہ آپ اسکرین کے نچلے حصے سے زیادہ قریب سے سوائپ نہ کریں"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"آپ نے واپس جائیں اشارے کو مکمل کر لیا۔"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"اس بات کو یقینی بنائیں کہ آپ اسکرین کے نچلے حصے سے زیادہ قریب سے سوائپ نہ کریں۔"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"پچھلے اشارے کی حساسیت تبدیل کرنے کے لیے ترتیبات پر جائیں"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"واپس جانے کے لیے سوائپ کریں"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"پچھلی اسکرین پر واپس جانے کے لیے بائیں یا دائیں کنارے سے اسکرین کے وسط تک سوائپ کریں۔"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"آخری اسکرین پر واپس جانے کے لیے، 2 انگلیوں سے بائیں یا دائیں کنارے سے اسکرین کے وسط تک سوائپ کریں۔"</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"واپس جائیں"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"دائیں یا بائیں کنارے سے اسکرین کے وسط تک سوائپ کریں"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"اس بات کو یقینی بنائیں کہ آپ اسکرین کے نچلے کنارے سے اوپر کی طرف سوائپ کریں"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"اس بات کو یقینی بنائیں کہ آپ اپنی انگلی اوپر اٹھانے سے پہلے موقوف نہ کریں"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"اس بات کو یقینی بنائیں کہ آپ سیدھا اوپر کی طرف سوائپ کریں"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"آپ نے ہوم پر جانے کا اشارہ مکمل کر لیا۔ اس کے بعد، واپس جانے کا طریقہ جانیں۔"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"آپ نے ہوم پر جانے کا اشارہ مکمل کر لیا"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"اس بات کو یقینی بنائیں کہ آپ اسکرین کے نچلے کنارے سے اوپر کی طرف سوائپ کریں۔"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"اس بات کو یقینی بنائیں کہ آپ اپنی انگلی اوپر اٹھانے سے پہلے موقوف نہ کریں۔"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"اس بات کو یقینی بنائیں کہ آپ سیدھا اوپر کی طرف سوائپ کریں۔"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"آپ نے ہوم پر جانے کا اشارہ مکمل کر لیا۔ اس کے بعد واپس جانے کا طریقہ جانیں۔"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"آپ نے ہوم پر جانے کا اشارہ مکمل کر لیا۔"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"ہوم پر جانے کے لیے سوائپ کریں"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"اپنی اسکرین کے نیچے سے اوپر کی طرف سوائپ کریں۔ یہ اشارہ آپ کو ہمیشہ ہوم اسکرین پر لے جاتا ہے۔"</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"اسکرین کے نیچے سے 2 انگلیوں سے اوپر سوائپ کریں۔ یہ اشارہ آپ کو ہمیشہ ہوم اسکرین پر لے جاتا ہے۔"</string> - <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"ہوم پر جائیں"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"اپنی اسکرین کے نچلے حصے سے اوپر کی طرف سوائپ کریں"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"بہترین!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"اس بات کو یقینی بنائیں کہ آپ اسکرین کے نچلے کنارے سے اوپر کی طرف سوائپ کریں"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"انگلی اٹھانے سے پہلے ونڈو کو زیادہ دیر تک پکڑنے کی کوشش کریں"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"اس بات کو یقینی بنائیں کہ آپ سیدھا اوپر کی طرف سوائپ کریں، پھر موقوف کریں"</string> + <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"گھر جائیں"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"کسی بھی وقت اپنی ہوم اسکرین پر جانے کے لیے، اپنی اسکرین کے نیچے سے اوپر سوائپ کریں"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"اس بات کو یقینی بنائیں کہ آپ اسکرین کے نچلے کنارے سے اوپر کی طرف سوائپ کریں۔"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"انگلی اٹھانے سے پہلے ونڈو کو زیادہ دیر تک پکڑنے کی کوشش کریں۔"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"اس بات کو یقینی بنائیں کہ آپ سیدھا اوپر کی طرف سوائپ کریں، پھر موقوف کریں۔"</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"آپ نے اشاروں کو استعمال کرنے کا طریقہ سیکھ لیا۔ اشاروں کو آف کرنے کے لیے ترتیبات پر جائیں۔"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"آپ نے ایپس کو سوئچ کرنے کا اشارہ مکمل کر لیا"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"آپ نے ایپس کو سوئچ کرنے کا اشارہ مکمل کر لیا۔"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ایپس سوئچ کرنے کے لیے سوائپ کریں"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ایپس کے مابین سوئچ کرنے کے لیے، اپنی اسکرین کے نچلے حصے سے اوپر کی جانب سوائپ کریں، پکڑے رکھیں، پھر چھوڑ دیں۔"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"ایپس کے مابین سوئچ کرنے کیلئے، اپنی اسکرین کے نیچے سے 2 انگلیوں سے اوپر سوائپ کریں، دبائے رکھیں پھر چھوڑ دیں۔"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ایپس سوئچ کریں"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"اپنی اسکرین کے نچلے حصے سے اوپر کی جانب سوائپ کریں، دبائے رکھیں، پھر چھوڑ دیں"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"بہت خوب!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"سب ہو گیا"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"ہو گیا"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ترتیبات"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ٹاشک بار دکھایا گیا"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ٹاسک بار چھپایا گیا"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"نیویگیشن بار"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"ہمیشہ ٹاسک بار دکھائیں"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"نیویگیشن موڈ تبدیل کریں"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ٹاسک بار ڈیوائیڈر"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"اوپر/بائیں طرف منتقل کریں"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"نیچے/دائیں طرف منتقل کریں"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# مزید ایپ دکھائیں۔}other{# مزید ایپس دکھائیں۔}}"</string> diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml index 9f00663887..cf70ebdde8 100644 --- a/quickstep/res/values-uz/strings.xml +++ b/quickstep/res/values-uz/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Ilova tavsiyalari yoqildi"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Endi ilova takliflari chiqmaydi"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Taklif etilgan ilova: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Qurilmangizni buring"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Ishorali navigatsiya darsligini tugatish uchun qurilmani buring"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Ekran chetidan boshlab oʻngdan yoki chapdan suring"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Ekranning oʻng yoki chap chetidan oʻrtasigacha suring va qoʻyib yuboring"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Ekran chetidan boshlab oʻngdan yoki chapdan suring."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Ekranning oʻng yoki chap chetidan oʻrtasiga suring va qoʻyib yuboring."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Ortga qaytish uchun oʻngdan surishni oʻrgandingiz. Endi ilovalarni almashtirishni oʻrganamiz."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Ortga qaytish ishorasi darsini tamomladingiz"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Barmoqni ekran pastiga yaqin surmaslikka harakat qiling"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Ortga qaytish ishorasi darsini tamomladingiz."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Barmoqni ekran pastiga yaqin surmaslikka harakat qiling."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Orqaga ishorasi sezuvchanligi Sozlamalardan oʻzgartiriladi"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Orqaga qaytish"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Ortga qaytish uchun barmoqni ekranning yon chekkalaridan oʻrtasigacha suring."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Oxirgi ekranga qaytish uchun 2 barmoq bilan ekranning chap yoki oʻng chekkasidan oʻrtasigacha suring."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Orqaga"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Chap yoki oʻng chetidan ekranning oʻrtasiga suring"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Barmoqni ekranning pastki chetidan yuqoriga suring."</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Barmoqni ekrandan pauzasiz qoʻyib uzing"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Barmoqni tik tepaga suring"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Bosh ekranni ochish ishorasi darsini tamomladingiz. Endi orqaga qaytishni oʻrganamiz."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Bosh ekranni ochish ishorasi darsini tamomladingiz"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Barmoqni ekranning pastki chetidan yuqoriga suring."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Barmoqni ekrandan pauzasiz qoʻyib uzing."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Barmoqni tik tepaga suring."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Bosh ekranni ochish ishorasi darsini tamomladingiz. Endi orqaga qaytishni oʻrganamiz."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Bosh ekranni ochish ishorasi darsini tamomladingiz."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Svayp bilan bosh ekranni ochish"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Ekranning pastidan tepaga qarab suring. Bu ishora doim Bosh ekranni ochadi."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"2 barmoq bilan ekranning quyidan tepasiga suring. Bu ishora har doim Bosh ekranni ochadi."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Boshiga"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Ekranning quyi qismidan tepaga torting"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Barakalla!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Barmoqni ekranning pastki chetidan yuqoriga suring."</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Barmoqni uzishdan oldin oynani biroz bosib turing"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Avval tik tepaga surib, keyin pauza qiling"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Istalgan vaqtda bosh ekranga oʻtish uchun ekranning pastidan tepaga suring"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Barmoqni ekranning pastki chetidan yuqoriga suring."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Barmoqni uzishdan oldin oynani biroz bosib turing."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Avval tik tepaga surib, keyin pauza qiling."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Ishoralardan qanday foydalanishni oʻrganib oldingiz. Ishoralarni oʻchirish uchun Sozlamalarga kiring."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Ilovalarni almashtirish darsini tamomladingiz"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Ilovalarni almashtirish darsini tamomladingiz."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Ilovalar orasida almashish"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Ilovalarni ochish uchun ekranning pastidan tepaga qarab suring, biroz ushlab turing va qoʻyib yuboring"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Ilovalarni almashtirish uchun 2 barmoq bilan ekranning quyidan tepasiga surib turib, qoʻyib yuboring"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Ilovalarni almashtirish"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Ekranning pastidan tepaga qarab suring, biroz ushlab turing va qoʻyib yuboring"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Juda soz!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Tayyor"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Tayyor"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Sozlamalar"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Vazifalar paneli ochiq"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Vazifalar paneli yopiq"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigatsiya paneli"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Doim vazifalar paneli chiqsin"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Navigatsiya rejimini oʻzgartirish"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Vazifalar panelini ajratkich"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Yuqoriga yoki chapga oʻtkazish"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pastga yoki oʻngga oʻtkazish"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Yana # ta ilovani chiqarish}other{Yana # ta ilovani chiqarish}}"</string> diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml index 48bc1cb158..ada07bd046 100644 --- a/quickstep/res/values-vi/strings.xml +++ b/quickstep/res/values-vi/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Đã bật tính năng Ứng dụng đề xuất"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Tính năng Ứng dụng đề xuất bị tắt"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Ứng dụng dự đoán: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Xoay thiết bị của bạn"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Vui lòng xoay thiết bị của bạn để hoàn tất hướng dẫn thao tác bằng cử chỉ"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Hãy vuốt từ mép ngoài cùng bên phải hoặc ngoài cùng bên trái"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Hãy vuốt từ mép phải hoặc mép trái tới giữa màn hình rồi nhấc ngón tay ra"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Hãy vuốt từ mép ngoài cùng bên phải hoặc ngoài cùng bên trái."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Hãy vuốt từ mép phải hoặc mép trái tới giữa màn hình rồi thả tay ra."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Bạn đã học được cách vuốt từ mép phải để quay lại. Tiếp theo, hãy tìm hiểu cách chuyển đổi ứng dụng."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Bạn đã thực hiện xong cử chỉ quay lại"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Hãy nhớ không được vuốt quá gần phần dưới cùng của màn hình"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Bạn đã thực hiện xong cử chỉ quay lại."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Hãy nhớ không được vuốt quá gần phần cuối màn hình."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Để thay đổi độ nhạy của cử chỉ quay lại, hãy vào mục Cài đặt"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Vuốt để quay lại"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Để quay lại màn hình gần đây nhất, hãy vuốt từ mép trái hoặc mép phải tới chính giữa màn hình."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Để quay lại màn hình trước đó, hãy vuốt 2 ngón tay từ cạnh trái hoặc phải vào giữa màn hình."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Quay lại"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Hãy vuốt từ mép trái hoặc mép phải tới giữa màn hình"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Hãy vuốt lên từ mép dưới cùng của màn hình"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Hãy nhớ không được dừng trước khi nhấc ngón tay"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Hãy vuốt thẳng lên"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Bạn đã thực hiện xong cử chỉ chuyển đến Màn hình chính. Tiếp theo, hãy tìm hiểu cách quay lại."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Bạn đã thực hiện xong cử chỉ chuyển đến Màn hình chính"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Hãy vuốt lên từ mép dưới cùng của màn hình."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Hãy nhớ không được tạm dừng trước khi nhấc ngón tay."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Hãy vuốt thẳng lên."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Bạn đã thực hiện xong cử chỉ chuyển đến Màn hình chính. Tiếp theo, hãy tìm hiểu cách quay lại."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Bạn đã thực hiện xong cử chỉ chuyển đến Màn hình chính."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Vuốt để chuyển đến Màn hình chính"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Vuốt lên từ cuối màn hình. Cử chỉ này luôn đưa bạn đến Màn hình chính."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Vuốt 2 ngón tay lên từ cuối màn hình. Cử chỉ này luôn đưa bạn về Màn hình chính."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Chuyển đến màn hình chính"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Vuốt lên từ mép dưới cùng của màn hình"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Tuyệt vời!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Hãy vuốt lên từ mép dưới cùng của màn hình"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Hãy thử giữ cửa sổ lâu hơn trước khi nhấc ngón tay ra"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Hãy vuốt thẳng lên rồi tạm dừng"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Để chuyển đến màn hình chính bất cứ lúc nào, hãy vuốt lên từ cuối màn hình"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Hãy vuốt lên từ mép dưới cùng của màn hình."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Hãy thử giữ cửa sổ lâu hơn trước khi thả tay ra."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Hãy vuốt thẳng lên, sau đó tạm dừng."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Bạn đã tìm hiểu cách sử dụng cử chỉ. Để tắt cử chỉ, hãy chuyển đến phần Cài đặt."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Bạn đã thực hiện xong cử chỉ chuyển đổi ứng dụng"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Bạn đã thực hiện xong cử chỉ chuyển đổi ứng dụng."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Vuốt để chuyển đổi ứng dụng"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Để chuyển đổi ứng dụng, hãy vuốt lên từ cuối màn hình, giữ rồi thả ra."</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Để chuyển đổi giữa các ứng dụng, hãy vuốt lên từ cuối màn hình, giữ rồi thả ra."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Để chuyển đổi giữa các ứng dụng, hãy vuốt 2 ngón tay lên từ cuối màn hình, giữ rồi thả ra."</string> - <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Chuyển đổi ứng dụng"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Vuốt lên từ mép dưới cùng của màn hình, giữ rồi nhấc ngón tay ra"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Rất tốt!"</string> + <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Chuyển đổi giữa các ứng dụng"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Đã hoàn tất"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Xong"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Cài đặt"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Đã hiện thanh thao tác"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Đã ẩn thanh thao tác"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Thanh điều hướng"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Luôn hiển thị Taskbar"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Thay đổi chế độ điều hướng"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Đường phân chia Taskbar"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Chuyển lên trên cùng/sang bên trái"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Chuyển xuống dưới cùng/sang bên phải"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Hiện thêm # ứng dụng.}other{Hiện thêm # ứng dụng.}}"</string> diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml index 80d75251a4..1e3c4ab0e4 100644 --- a/quickstep/res/values-zh-rCN/strings.xml +++ b/quickstep/res/values-zh-rCN/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"已启用应用建议"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"已停用应用建议"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"预测的应用:<xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"请旋转设备"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"请旋转设备,完成手势导航教程"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"确保从最右侧或最左侧边缘开始滑动"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"确保从右侧或左侧边缘滑动到屏幕中间位置后再松开手指"</string> - <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"您已了解如何使用“从右侧向左滑动”手势返回。接下来学习切换应用吧!"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"您完成了“返回”手势"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"确保滑动时手的位置不要太靠近屏幕底部"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"请从最右侧或最左侧边缘开始滑动。"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"请从右侧或左侧边缘滑动到屏幕中间位置后再松开手指。"</string> + <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"您已了解如何使用“从右侧向左滑动”手势返回。接下来了解如何切换应用。"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"您完成了“返回”手势教程。"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"请确保滑动时手的位置不要太靠近屏幕底部。"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"如要调节“返回”手势的灵敏度,请转到“设置”"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"滑动即可返回"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"如要返回上一个屏幕,请从屏幕左侧或右侧边缘往屏幕中间滑动。"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"若要返回上一个屏幕,请用两根手指从屏幕左侧或右侧边缘向中间滑动。"</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"返回"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"从屏幕左侧或右侧边缘滑动到中间"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"确保从屏幕底部边缘向上滑动"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"松开手指前,请勿中途停顿"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"确保笔直向上滑动"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"您完成了“转到主屏幕”手势。接下来了解如何返回。"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"您完成了“转到主屏幕”手势"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"请确保从屏幕底部边缘向上滑动。"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"松开手指前,请确保不要停下来。"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"请确保直接向上滑动。"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"您完成了“转到主屏幕”手势教程。接下来了解如何返回。"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"您完成了“转到主屏幕”手势教程。"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"上滑可转到主屏幕"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"从屏幕底部向上滑动,即可随时回到主屏幕。"</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"用两根手指从屏幕底部向上滑动,这个手势会一律使您回到主屏幕。"</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"前往主屏幕"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"从屏幕底部向上滑动"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"太棒了!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"确保从屏幕底部边缘向上滑动"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"尝试按住窗口较长时间,然后再松开手指"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"确保笔直向上滑动,然后停住"</string> - <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"您已了解如何使用手势了。如要关闭手势,请前往“设置”。"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"您完成了应用切换手势"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"若要随时前往主屏幕,请从屏幕的底部向上滑动"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"请确保从屏幕底部边缘向上滑动。"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"请尝试按住窗口较长时间,然后再松开手指。"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"请确保直接向上滑动,然后停住。"</string> + <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"您已了解如何使用手势了。如要关闭手势,请转到“设置”。"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"您完成了“切换应用”手势教程。"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"滑动即可切换应用"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"如需在应用之间切换,请从屏幕底部向上滑动,按住,然后松开。"</string> - <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"如需在应用之间切换,请从屏幕底部向上滑动,按住,然后松开。"</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"如需在应用之间切换,请从屏幕底部向上滑动后按住,然后松开。"</string> + <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"若要在应用之间切换,请用两根手指从屏幕底部向上滑动并按住,然后松开。"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"切换应用"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"从屏幕底部向上滑动后按住,然后松开"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"恭喜!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"大功告成"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"完成"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"设置"</string> @@ -86,7 +80,7 @@ <string name="gesture_tutorial_nice" msgid="2936275692616928280">"很好!"</string> <string name="gesture_tutorial_step" msgid="1279786122817620968">"教程 <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string> <string name="allset_title" msgid="5021126669778966707">"大功告成!"</string> - <string name="allset_hint" msgid="459504134589971527">"向上滑动可前往主屏幕"</string> + <string name="allset_hint" msgid="459504134589971527">"向上滑动可转到主屏幕"</string> <string name="allset_button_hint" msgid="2395219947744706291">"点按主屏幕按钮即可前往主屏幕"</string> <string name="allset_description_generic" msgid="5385500062202019855">"您可以开始使用<xliff:g id="DEVICE">%1$s</xliff:g>了"</string> <string name="default_device_name" msgid="6660656727127422487">"设备"</string> @@ -103,7 +97,7 @@ <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"跳过"</string> <string name="accessibility_rotate_button" msgid="4771825231336502943">"旋转屏幕"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"任务栏教程"</string> - <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"将一个应用拖到一侧,即可一次使用两个应用"</string> + <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"将一个应用拖动到一侧,即可一次使用两个应用"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"缓慢向上滑动即可显示任务栏"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"根据您的日常安排获取应用建议"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"在设置中开启手势导航后,任务栏会自动隐藏"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"任务栏已显示"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"任务栏已隐藏"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"导航栏"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"始终显示任务栏"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"更改导航模式"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"任务栏分隔线"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到顶部/左侧"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右侧"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{显示另外 # 个应用。}other{显示另外 # 个应用。}}"</string> diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml index 5eaf95e94b..432352aa3a 100644 --- a/quickstep/res/values-zh-rHK/strings.xml +++ b/quickstep/res/values-zh-rHK/strings.xml @@ -30,11 +30,11 @@ <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"少於 1 分鐘"</string> <string name="time_left_for_app" msgid="3111996412933644358">"今天剩餘時間:<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="title_app_suggestions" msgid="4185902664111965088">"應用程式建議"</string> - <string name="all_apps_prediction_tip" msgid="2672336544844936186">"你的預測應用程式"</string> + <string name="all_apps_prediction_tip" msgid="2672336544844936186">"您的預測應用程式"</string> <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"在主畫面底部取得應用程式建議"</string> <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"在主畫面「我的最愛」列取得應用程式建議"</string> - <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"在主畫面輕鬆存取常用的應用程式。系統會根據你的日常安排更改建議,並將底部的應用程式移到主畫面。"</string> - <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"在主畫面輕鬆存取最常用的應用程式。系統會根據你的日常安排變更建議,「我的最愛」列中的應用程式會移至主畫面。"</string> + <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"在主畫面輕鬆存取常用的應用程式。系統會根據您的日常安排更改建議,並將底部的應用程式移到主畫面。"</string> + <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"在主畫面輕鬆存取最常用的應用程式。系統會根據您的日常安排變更建議,「我的最愛」列中的應用程式會移至主畫面。"</string> <string name="hotseat_edu_accept" msgid="1611544083278999837">"取得應用程式建議"</string> <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"不用了,謝謝"</string> <string name="hotseat_prediction_settings" msgid="6246554993566070818">"設定"</string> @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"已啟用應用程式建議"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"已停用應用程式建議"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"預測應用程式:<xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"旋轉裝置方向"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"請旋轉裝置方向以完成手勢導覽教學課程"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"請確保從螢幕最右側或最左側邊緣滑動"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"請確保從螢幕右側或左側邊緣往中央滑動,然後放開手指"</string> - <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"你已瞭解如何透過「由右向左滑動」手勢返回。接下來一起瞭解如何切換應用程式。"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"你已完成「返回」手勢的教學課程"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"滑動時,手的位置不要太接近螢幕底部"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"請從螢幕右側或左側邊緣滑動。"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"請從螢幕右側或左側邊緣往中央滑動,然後放開手指。"</string> + <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"您已瞭解如何透過「由右向左滑動」手勢返回。接下來一起瞭解如何切換應用程式。"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"您已完成「返回」手勢的教學課程。"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"滑動時,手的位置不要太接近螢幕底部。"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"如要變更「返回」手勢的敏感度,請前往「設定」"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"滑動即可返回"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"如要返回上一個畫面,請從螢幕左側或右側邊緣往中央滑動。"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"如要返回上一個畫面,請用 2 隻手指從螢幕左側或右側邊緣往中央滑動。"</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"返回"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"從螢幕左側或右側邊緣往中央滑動"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"請確保從螢幕底部邊緣向上滑動"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"放開手指前請勿停下來"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"請確保向上滑動"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"你已完成「返回主畫面」手勢的教學課程。接著,一起來瞭解如何返回上一個畫面。"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"你已完成「返回主畫面」手勢的教學課程"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"請從螢幕底部邊緣向上滑動。"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"放開手指前請勿停下來。"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"請向上滑動。"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"您已完成「返回主畫面」手勢的教學課程。接著,一起來瞭解如何返回上一個畫面。"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"您已完成「返回主畫面」手勢的教學課程。"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"向上滑動即可返回主畫面"</string> - <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"從螢幕底部向上滑動。這個手勢在所有畫面下都可讓你返回主畫面。"</string> - <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"請用 2 隻手指從螢幕底部向上滑動。這個手勢在所有畫面下都可讓你返回主畫面。"</string> + <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"從螢幕底部向上滑動。這個手勢在所有畫面下都可讓您返回主畫面。"</string> + <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"請用 2 隻手指從螢幕底部向上滑動。這個手勢在所有畫面下都可讓您返回主畫面。"</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"返回主畫面"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"從螢幕底部向上滑動"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"太好了!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"請確保從螢幕底部邊緣向上滑動"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"請嘗試按住視窗更長時間,然後再放開"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"請向上滑動,然後停住"</string> - <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"你已學會如何使用手勢。如要關閉手勢,請前往「設定」。"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"你已完成「切換應用程式」手勢的教學課程"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"只要從螢幕底部向上滑動,隨時可以返回主畫面"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"請從螢幕底部邊緣向上滑動。"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"請嘗試按住視窗更長時間,然後再放開。"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"請向上滑動,然後停住。"</string> + <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"您已學會如何使用手勢。如要關閉手勢,請前往「設定」。"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"您已完成「切換應用程式」手勢的教學課程。"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"滑動即可切換應用程式"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"如要切換應用程式,請從螢幕底部向上滑動並按住,然後放開。"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"如要切換應用程式,請用 2 隻手指從螢幕底部向上滑動並按住,然後放開手指。"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"切換應用程式"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"從螢幕底部向上滑動並按住,然後放開"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"做得好!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"大功告成"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"完成"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"設定"</string> @@ -88,7 +82,7 @@ <string name="allset_title" msgid="5021126669778966707">"設定完成!"</string> <string name="allset_hint" msgid="459504134589971527">"向上滑動即可前往主畫面"</string> <string name="allset_button_hint" msgid="2395219947744706291">"輕按主按鈕即可前往主畫面"</string> - <string name="allset_description_generic" msgid="5385500062202019855">"你可以開始使用<xliff:g id="DEVICE">%1$s</xliff:g>了"</string> + <string name="allset_description_generic" msgid="5385500062202019855">"您可以開始使用 <xliff:g id="DEVICE">%1$s</xliff:g> 了"</string> <string name="default_device_name" msgid="6660656727127422487">"裝置"</string> <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系統導覽設定"</annotation></string> <string name="action_share" msgid="2648470652637092375">"分享"</string> @@ -96,18 +90,18 @@ <string name="action_split" msgid="2098009717623550676">"分割"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"輕按其他應用程式以使用分割螢幕"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"選擇其他應用程式才能使用分割螢幕"</string> - <string name="blocked_by_policy" msgid="2071401072261365546">"應用程式或你的機構不允許此操作"</string> + <string name="blocked_by_policy" msgid="2071401072261365546">"應用程式或您的機構不允許此操作"</string> <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"要略過手勢操作教學課程嗎?"</string> - <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"你之後可以在「<xliff:g id="NAME">%1$s</xliff:g>」應用程式找到這些說明"</string> + <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"您之後可以在「<xliff:g id="NAME">%1$s</xliff:g>」應用程式找到這些說明"</string> <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"取消"</string> <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"略過"</string> <string name="accessibility_rotate_button" msgid="4771825231336502943">"旋轉螢幕"</string> <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"工作列教學"</string> <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"將應用程式拖曳到一邊,即可同時使用 2 個應用程式"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"慢慢向上滑動即可顯示工作列"</string> - <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"根據你的日常安排提供應用程式建議"</string> + <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"根據您的日常安排提供應用程式建議"</string> <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"在「設定」中啟用手勢操作後,工作列就會自動隱藏"</string> - <string name="taskbar_edu_features" msgid="3320337287472848162">"工作列助你事半功倍"</string> + <string name="taskbar_edu_features" msgid="3320337287472848162">"工作列助您事半功倍"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"關閉"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"完成"</string> <string name="taskbar_button_home" msgid="2151398979630664652">"住宅"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"顯示咗工作列"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"隱藏咗工作列"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"導覽列"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"一律顯示工作列"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"變更導覽模式"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"工作列分隔線"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移至上方/左側"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移至底部/右側"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{顯示另外 # 個應用程式。}other{顯示另外 # 個應用程式。}}"</string> diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml index b7e6e06014..6926ec5603 100644 --- a/quickstep/res/values-zh-rTW/strings.xml +++ b/quickstep/res/values-zh-rTW/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"應用程式建議功能已啟用"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"應用程式建議功能已停用"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"預測的應用程式:<xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"旋轉裝置螢幕方向"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"如要完成手勢操作教學課程,請旋轉裝置螢幕方向"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"請務必從螢幕最右側或最左側滑動"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"請務必從螢幕右側或左側往中央滑動,然後放開手指"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"請從螢幕右側或左側邊緣滑動。"</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"請從螢幕右側或左側邊緣往中央滑動,然後放開手指。"</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"你已瞭解如何透過「由右向左滑動」手勢返回。接著,一起來瞭解如何切換應用程式。"</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"你已完成「返回」手勢的教學課程"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"滑動時,手的位置不要太接近螢幕底部"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"你已完成「返回」手勢的教學課程。"</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"滑動時,手的位置不要太接近螢幕底部。"</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"如要變更「返回」手勢的敏感度,請前往「設定」"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"滑動即可返回"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"如要返回上一個畫面,請從螢幕左側或右側邊緣往中央滑動。"</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"如要返回上一個畫面,請用 2 指從螢幕左側或右側邊緣往中央滑動。"</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"返回"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"從螢幕右側或左側往中央滑動"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"請務必從螢幕底部向上滑動"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"放開手指前請勿停下來"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"請務必向上滑動"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"你已完成「返回主畫面」手勢教學課程,接著一起來瞭解如何返回上一個畫面。"</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"你已完成「返回主畫面」手勢的教學課程"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"請從螢幕底部邊緣向上滑動。"</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"放開手指前請勿停下來。"</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"請向上滑動。"</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"你已完成「返回主畫面」手勢的教學課程。接著,一起來瞭解如何返回上一個畫面。"</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"你已完成「返回主畫面」手勢的教學課程。"</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"使用滑動手勢返回主畫面"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"從螢幕底部向上滑動,即可返回主畫面。"</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"用 2 指從螢幕底部向上滑動,即可回到主畫面。"</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"返回主畫面"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"從螢幕底部向上滑動"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"太棒了!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"請務必從螢幕底部向上滑動"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"請按住視窗久一點再放開"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"請務必向上滑動,然後停住"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"只要從螢幕底部向上滑動,隨時可以返回主畫面"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"請從螢幕底部邊緣向上滑動。"</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"請按住視窗久一點,然後再放開。"</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"請直直向上滑動,然後停住。"</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"你已瞭解如何使用手勢了。如要關閉手勢,請前往「設定」。"</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"你已完成「切換應用程式」手勢的教學課程"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"你已完成「切換應用程式」手勢的教學課程。"</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"使用滑動手勢切換應用程式"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"如要切換不同的應用程式,請從螢幕底部向上滑動並按住,然後放開手指。"</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"如要切換應用程式,請用 2 指從螢幕底部向上滑動並按住,然後放開手指。"</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"切換應用程式"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"從螢幕底部向上滑動並按住,然後放開手指"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"非常好!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"大功告成"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"完成"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"設定"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"已顯示工作列"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"已隱藏工作列"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"導覽列"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"一律顯示工作列"</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"變更操作模式"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"工作列分隔線"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到上方/左側"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右側"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{顯示另外 # 個應用程式。}other{顯示另外 # 個應用程式。}}"</string> diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml index 9ca2106bd3..246b1216b2 100644 --- a/quickstep/res/values-zu/strings.xml +++ b/quickstep/res/values-zu/strings.xml @@ -44,41 +44,35 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Iziphakamiso zohlelo lokusebenza zinikwe amandla"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Iziphakamiso zohlelo lokusebenza zikhutshaziwe"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Uhlelo lokusebenza olubikezelwe: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Zungezisa idivayisi yakho"</string> - <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Sicela uzungezise idivayisi yakho ukuze uqedele okokufundisa kokufuna ngokuthinta"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Qinisekisa ukuthi uswayipha ukusuka onqenqemeni olukude ngakwesokudla noma olukude ngakwesokunxele"</string> - <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Qinisekisa ukuthi uswayipha ukusuka kunqenqema olungakwesokudla noma olungakwesokunxele ukuya maphakathi nesikrini bese uyadedela."</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Qiniseka ukuthi uswayipha kusuka onqenqemeni olukude ngakwesokudla noma olukude ngakwesokunxele."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Qiniseka ukuthi uswayipha kusuka kunqenqema ongakwesokudla noma ongakwesokunxele kuya maphakathi nesikrini bese uyadedela."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Ufunde indlela yokuswayipha kusuka kwesokudla ukuze ubuyele emuva. Ngokulandelayo, funda indlela yokushintsha ama-app."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Ukuqedile ukuthinta kokubuyela emuva"</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Qinisekisa ukuba awuswayipheli eduze kakhulu naphansi kwesikrini"</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Ukuqedile ukuthinta kokubuyela emuva."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Qiniseka ukuba awuswayipheli eduze kakhulu naphansi kwesikrini."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Ukuze ushintshe ukuzwela kokuthinta emuva, iya Kumasethingi"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"Swayipha ukuze uye emuva"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Ukuze ubuyele emuva esikrinini sokugcina, swapha kusuka emngceleni wesobunxele noma wesokudla kuya phakathi kwesikrini."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Ukuze ubuyele esikrinini sokugcina, swayipha ngeminwe emi-2 ukusuka kwesokunxele noma kwesokudla emphethweni uye phakathi kwesikrini."</string> <string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Iya emuva"</string> - <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Swayipha ukusuka kwesokunxele noma kwesokudla ukuya phakathi kwesikrini"</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Qiniseka ukuthi uswayiphela phezulu ukusuka emngceleni ophansi wesikrini"</string> - <string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Qinisekisa ukuthi awumisi ngaphambi kokudedela"</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Qiniseka ukuthi uswayiphela ngqo phezulu"</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Ukuqedile ukuthinta kokuya ekhaya. Ngokulandelayo, funda indlela yokuya emuva."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Ukuqedile ukuthinta kokuya ekhaya"</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Qiniseka ukuthi uswayiphela phezulu kusuka emngceleni ophansi wesikrini."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Qiniseka ukuthi awumisi ngaphambi kokudedela."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Qiniseka ukuthi uswayiphela ngqo phezulu."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Ukuqedile ukuthinta kokuya Ekhaya. Ngokulandelayo, funda indlela yokuya emuva."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Ukuqedile ukuthinta kokuya Ekhaya."</string> <string name="home_gesture_intro_title" msgid="836590312858441830">"Swayipha ukuze uye ekhaya"</string> <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swayiphela phezulu kusuka phansi kwesikrini sakho.Lokhu kuthinta kuhlala kukusa esikrinini sasekhaya."</string> <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Swayiphela phezulu ngeminwe emi-2 kusukela phansi esikrinini. Lesi senzo sihlala sikuyisa esikrinini Sasekhaya."</string> <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Iya ekhasini lokuqala"</string> - <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Swayiphela phezulu ukusuka phansi esikrinini sakho"</string> - <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Umsebenzi omuhle!"</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Qiniseka ukuthi uswayiphela phezulu ukusuka emngceleni ophansi wesikrini"</string> - <string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Zama ukubamba iwindi isikhashana ngaphambi kokulidedela"</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Qinisekisa ukuthi uswayiphela ngqo phezulu bese uyamisa"</string> + <string name="home_gesture_tutorial_subtitle" msgid="8979014952569486971">"Ukuze uye esikrinini sakho sasekhaya noma kunini, swayiphela phezulu ukusuka phansi esikrinini sakho"</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Qiniseka ukuthi uswayiphela phezulu kusuka emngceleni ophansi wesikrini."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Zama ukubamba iwindi isikhashana ngaphambi kokulidedela."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Qiniseka ukuthi uswayiphela ngqo phezulu bese uyamisa."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Ufunde ukusebenzisa ukuthinta. Ukuze uvale ukuthinta, iya kokuthi Amasethingi."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Ukuqedile ukuthinta kokushintsha ama-app"</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Ukuqedile ukuthinta kokushintsha ama-app."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swayipha ukuze ushintshe ama-app"</string> <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Ukuze ushintshe phakathi kwama-app, swayiphela phezulu kusuka ngezansi kwesikrini sakho, bese uyadedela."</string> <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Ukuze ushintshe phakathi kwama-app, swayiphela phezulu ngeminwe emi-2 kusukela phansi esikrinini sakho, ubambe, bese uyakhulula."</string> <string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Shintsha ama-app"</string> - <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Swayiphela phezulu ukusuka phansi esikrinini sakho, ubambe, bese uyadedela"</string> - <string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Wenze kahle!"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Konke kusethiwe"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Kwenziwe"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Amasethingi"</string> @@ -121,9 +115,6 @@ <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Ibha yomsebenzi ibonisiwe"</string> <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Ibha yomsebenzi ifihliwe"</string> <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Ibha yokufuna"</string> - <string name="always_show_taskbar" msgid="3608801276107751229">"Bonisa i-Taskbar njalo."</string> - <string name="change_navigation_mode" msgid="9088393078736808968">"Shintsha imodi yokufuna"</string> - <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Isihlukanisi se-Taskbar"</string> <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Hamba phezulu/kwesokunxele"</string> <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Hamba phansi/kwesokudla"</string> <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Bonisa i-app e-# ngaphezulu.}one{Bonisa ama-app angu-# ngaphezulu.}other{Bonisa ama-app angu-# ngaphezulu.}}"</string> diff --git a/quickstep/res/values/attrs.xml b/quickstep/res/values/attrs.xml index fb51919b57..7288774075 100644 --- a/quickstep/res/values/attrs.xml +++ b/quickstep/res/values/attrs.xml @@ -26,7 +26,8 @@ --> <declare-styleable name="TaskView"> <!-- Border color for a keyboard quick switch task views --> - <attr name="borderColor" format="color" /> + <attr name="focusBorderColor" format="color" /> + <attr name="hoverBorderColor" format="color" /> </declare-styleable> <!-- diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml index 36c7352781..1b5b0ee4db 100644 --- a/quickstep/res/values/colors.xml +++ b/quickstep/res/values/colors.xml @@ -25,6 +25,10 @@ <!-- Taskbar --> <color name="taskbar_nav_icon_selection_ripple">#E0E0E0</color> + <color name="taskbar_nav_icon_light_color_on_home">#ffffff</color> + <!-- The dark navigation button color is only used in the rare cases that taskbar isn't drawing + its background and the underlying app has requested dark buttons. --> + <color name="taskbar_nav_icon_dark_color_on_home">#99000000</color> <color name="taskbar_stashed_handle_light_color">#EBffffff</color> <color name="taskbar_stashed_handle_dark_color">#99000000</color> diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index bb4f74d2eb..b0244181e2 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -38,6 +38,8 @@ <dimen name="task_thumbnail_icon_size">48dp</dimen> <!-- The icon size for the focused task, placed in center of touch target --> <dimen name="task_thumbnail_icon_drawable_size">44dp</dimen> + <!-- The border width shown when task is hovered --> + <dimen name="task_hover_border_width">4dp</dimen> <!-- The space under the focused task icon --> <dimen name="overview_task_margin">16dp</dimen> <!-- The horizontal space between tasks --> @@ -269,6 +271,9 @@ <dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen> <dimen name="floating_rotation_button_taskbar_bottom_margin">10dp</dimen> + <!-- Copied from frameworks/base/packages/SystemUI --> + <dimen name="navigation_home_handle_width">108dp</dimen> + <!-- Taskbar --> <dimen name="taskbar_size">@*android:dimen/taskbar_frame_height</dimen> <dimen name="taskbar_ime_size">48dp</dimen> @@ -323,6 +328,12 @@ <!-- Taskbar swipe down threshold --> <dimen name="taskbar_to_nav_threshold">24dp</dimen> + <!-- Taskbar swipe up threshold multipliers --> + <item name="taskbar_nav_threshold_mult" format="float" type="dimen">4.5</item> + <item name="taskbar_app_window_threshold_mult" format="float" type="dimen">10</item> + <item name="taskbar_home_overview_threshold_mult" format="float" type="dimen">18</item> + <item name="taskbar_catch_up_threshold_mult" format="float" type="dimen">30</item> + <!-- Taskbar 3 button spacing --> <dimen name="taskbar_button_space_inbetween">24dp</dimen> <dimen name="taskbar_button_space_inbetween_phone">40dp</dimen> @@ -356,13 +367,22 @@ <dimen name="bubblebar_stashed_size">@dimen/transient_taskbar_stashed_height</dimen> <dimen name="bubblebar_stashed_handle_height">@dimen/taskbar_stashed_handle_height</dimen> <dimen name="bubblebar_pointer_size">8dp</dimen> + <dimen name="bubblebar_elevation">1dp</dimen> <dimen name="bubblebar_icon_size">50dp</dimen> <dimen name="bubblebar_badge_size">24dp</dimen> <dimen name="bubblebar_icon_overlap">12dp</dimen> + <dimen name="bubblebar_overflow_inset">24dp</dimen> <dimen name="bubblebar_icon_spacing">3dp</dimen> <dimen name="bubblebar_icon_elevation">1dp</dimen> + <!-- Bubble bar dismiss view --> + <dimen name="bubblebar_dismiss_target_size">96dp</dimen> + <dimen name="bubblebar_dismiss_target_small_size">60dp</dimen> + <dimen name="bubblebar_dismiss_target_icon_size">24dp</dimen> + <dimen name="bubblebar_dismiss_target_bottom_margin">50dp</dimen> + <dimen name="bubblebar_dismiss_floating_gradient_height">548dp</dimen> + <!-- Launcher splash screen --> <!-- Note: keep this value in sync with the WindowManager/Shell dimens.xml --> <!-- starting_surface_exit_animation_window_shift_length --> @@ -383,4 +403,12 @@ <dimen name="keyboard_quick_switch_task_view_radius">16dp</dimen> <dimen name="keyboard_quick_switch_no_recent_items_icon_size">24dp</dimen> <dimen name="keyboard_quick_switch_no_recent_items_icon_margin">8dp</dimen> + + <!-- Desktop mode --> + <dimen name="desktop_mode_floating_app_select_height">56dp</dimen> + <dimen name="desktop_mode_floating_app_select_elevation">4dp</dimen> + <dimen name="desktop_mode_floating_app_select_margin">16dp</dimen> + <dimen name="desktop_mode_floating_app_select_text_size">14sp</dimen> + <dimen name="desktop_mode_floating_app_select_text_margin">8dp</dimen> + </resources> diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml index 4f472f0625..df32626e5c 100644 --- a/quickstep/res/values/override.xml +++ b/quickstep/res/values/override.xml @@ -25,6 +25,14 @@ <string name="model_delegate_class" translatable="false">com.android.launcher3.model.QuickstepModelDelegate</string> + <string name="nav_handle_long_press_handler_class" translatable="false"></string> + + <string name="assist_utils_class" translatable="false"></string> + <string name="secondary_display_predictions_class" translatable="false">com.android.launcher3.secondarydisplay.SecondaryDisplayPredictionsImpl</string> + <string name="taskbar_model_callbacks_factory_class" translatable="false">com.android.launcher3.taskbar.TaskbarModelCallbacksFactory</string> + + <string name="assist_state_manager_class" translatable="false"></string> + </resources> diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index 6d0dbaea9f..dd9fc6371b 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -230,6 +230,7 @@ <string name="action_split">Split</string> <!-- Label for toast with instructions for split screen selection mode. [CHAR_LIMIT=50] --> <string name="toast_split_select_app">Tap another app to use split screen</string> + <string name="toast_split_select_cont_desc">Exit split screen selection</string> <!-- Label for toast when app selected for split isn't supported. [CHAR_LIMIT=50] --> <string name="toast_split_app_unsupported">Choose another app to use split screen</string> <!-- Message shown when an action is blocked by a policy enforced by the app or the organization managing the device. [CHAR_LIMIT=NONE] --> @@ -308,4 +309,10 @@ <!-- Accessibility label for quick switch tiles showing split tasks [CHAR LIMIT=NONE] --> <string name="quick_switch_split_task"><xliff:g id="app_name_1" example="Chrome">%1$s</xliff:g> and <xliff:g id="app_name_2" example="Gmail">%2$s</xliff:g></string> + + <!-- ******* Desktop ******* --> + <!-- Text shown in popup to choose a desktop app. [CHAR LIMIT=60] --> + <string name="desktop_select_app_toast">Adding app to Desktop</string> + <!-- Text shown on a button that closes the popup for choosing a desktop app. [CHAR_LIMIT=40] --> + <string name="desktop_button_close_app_toast">Cancel</string> </resources> diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml index 21b7fd5139..fc0370421d 100644 --- a/quickstep/res/values/styles.xml +++ b/quickstep/res/values/styles.xml @@ -298,4 +298,9 @@ <style name="rotate_prompt_subtitle" parent="TextAppearance.GestureTutorial.Dialog.Subtitle"> <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item> </style> + + <style name="ArrowTipTaskbarStyle"> + <item name="arrowTipBackground">?androidprv:attr/materialColorSurfaceContainer</item> + <item name="arrowTipTextColor">?androidprv:attr/materialColorOnSurface</item> + </style> </resources> diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java index 28bd701a48..f64b5cfafe 100644 --- a/quickstep/src/com/android/launcher3/LauncherInitListener.java +++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java @@ -15,23 +15,16 @@ */ package com.android.launcher3; -import android.animation.AnimatorSet; import android.annotation.TargetApi; import android.os.Build; -import android.os.CancellationSignal; -import android.view.RemoteAnimationTarget; -import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.quickstep.util.ActivityInitListener; -import com.android.quickstep.util.RemoteAnimationProvider; import java.util.function.BiPredicate; @TargetApi(Build.VERSION_CODES.P) public class LauncherInitListener extends ActivityInitListener<Launcher> { - private RemoteAnimationProvider mRemoteAnimationProvider; - /** * @param onInitListener a callback made when the activity is initialized. The callback should * return true to continue receiving callbacks (ie. for if the activity is @@ -43,37 +36,7 @@ public class LauncherInitListener extends ActivityInitListener<Launcher> { @Override public boolean handleInit(Launcher launcher, boolean alreadyOnHome) { - if (mRemoteAnimationProvider != null) { - QuickstepTransitionManager appTransitionManager = - ((QuickstepLauncher) launcher).getAppTransitionManager(); - - // Set a one-time animation provider. After the first call, this will get cleared. - // TODO: Probably also check the intended target id. - CancellationSignal cancellationSignal = new CancellationSignal(); - appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() { - @Override - public AnimatorSet createWindowAnimation(RemoteAnimationTarget[] appTargets, - RemoteAnimationTarget[] wallpaperTargets) { - - // On the first call clear the reference. - cancellationSignal.cancel(); - RemoteAnimationProvider provider = mRemoteAnimationProvider; - mRemoteAnimationProvider = null; - - if (provider != null && launcher.getStateManager().getState().overviewUi) { - return provider.createWindowAnimation(appTargets, wallpaperTargets); - } - return null; - } - }, cancellationSignal); - } launcher.deferOverlayCallbacksUntilNextResumeOrStop(); return super.handleInit(launcher, alreadyOnHome); } - - @Override - public void unregister() { - mRemoteAnimationProvider = null; - super.unregister(); - } } diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 75f25ac7c2..741249d53e 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -31,6 +31,12 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; import static android.window.TransitionFilter.CONTAINER_ORDER_TOP; +import static com.android.app.animation.Interpolators.ACCELERATE_1_5; +import static com.android.app.animation.Interpolators.AGGRESSIVE_EASE; +import static com.android.app.animation.Interpolators.DECELERATE_1_5; +import static com.android.app.animation.Interpolators.DECELERATE_1_7; +import static com.android.app.animation.Interpolators.EXAGGERATED_EASE; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.BaseActivity.INVISIBLE_ALL; import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS; import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS; @@ -42,12 +48,6 @@ import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.Utilities.mapBoundToRange; -import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; -import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; -import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5; -import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7; -import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.config.FeatureFlags.ENABLE_BACK_SWIPE_HOME_ANIMATION; import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAUNCH; import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION; @@ -60,6 +60,8 @@ import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATI import static com.android.launcher3.views.FloatingIconView.getFloatingIconView; import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch; +import static com.android.systemui.shared.system.InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME; +import static com.android.systemui.shared.system.InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME_FALLBACK; import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius; import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows; @@ -82,7 +84,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.os.CancellationSignal; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -140,7 +141,6 @@ import com.android.quickstep.util.MultiValueUpdateListener; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.RectFSpringAnim.DefaultSpringConfig; import com.android.quickstep.util.RectFSpringAnim.TaskbarHotseatSpringConfig; -import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.StaggeredWorkspaceAnim; import com.android.quickstep.util.SurfaceTransaction; import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties; @@ -151,6 +151,7 @@ import com.android.quickstep.views.FloatingWidgetView; import com.android.quickstep.views.RecentsView; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.DelegateLaunchAnimatorController; +import com.android.systemui.animation.LaunchableView; import com.android.systemui.animation.RemoteAnimationDelegate; import com.android.systemui.shared.system.BlurUtils; import com.android.systemui.shared.system.InteractionJankMonitorWrapper; @@ -158,6 +159,7 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; import com.android.wm.shell.startingsurface.IStartingWindowListener; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @@ -182,7 +184,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION = "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"; - private static final long APP_LAUNCH_DURATION = 500; + public static final long APP_LAUNCH_DURATION = 500; private static final long APP_LAUNCH_ALPHA_DURATION = 50; private static final long APP_LAUNCH_ALPHA_START_DELAY = 25; @@ -228,7 +230,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener private DeviceProfile mDeviceProfile; - private RemoteAnimationProvider mRemoteAnimationProvider; // Strong refs to runners which are cleared when the launcher activity is destroyed private RemoteAnimationFactory mWallpaperOpenRunner; private RemoteAnimationFactory mAppLaunchRunner; @@ -285,7 +286,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener mOpeningXInterpolator = AnimationUtils.loadInterpolator(context, R.interpolator.app_open_x); mOpeningInterpolator = AnimationUtils.loadInterpolator(context, - R.interpolator.three_point_fast_out_extra_slow_in); + R.interpolator.emphasized_interpolator); } @Override @@ -467,15 +468,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener return bounds; } - public void setRemoteAnimationProvider(final RemoteAnimationProvider animationProvider, - CancellationSignal cancellationSignal) { - mRemoteAnimationProvider = animationProvider; - cancellationSignal.setOnCancelListener(() -> { - if (animationProvider == mRemoteAnimationProvider) { - mRemoteAnimationProvider = null; - } - }); - } + /** Dump debug logs to bug report. */ + public void dump(@NonNull String prefix, @NonNull PrintWriter printWriter) {} /** * Content is everything on screen except the background and the floating view (if any). @@ -562,7 +556,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(view, SCALE_PROPERTY, scales) .setDuration(CONTENT_SCALE_DURATION); - scaleAnim.setInterpolator(DEACCEL_1_5); + scaleAnim.setInterpolator(DECELERATE_1_5); launcherAnimator.play(scaleAnim); }); @@ -580,7 +574,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener ObjectAnimator scrim = ObjectAnimator.ofArgb(scrimView, VIEW_BACKGROUND_COLOR, colors); scrim.setDuration(CONTENT_SCRIM_DURATION); - scrim.setInterpolator(DEACCEL_1_5); + scrim.setInterpolator(DECELERATE_1_5); launcherAnimator.play(scrim); } @@ -1230,7 +1224,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener * ie. pressing home, swiping up from nav bar. */ RemoteAnimationFactory createWallpaperOpenRunner(boolean fromUnlock) { - return new WallpaperOpenLauncherAnimationRunner(mHandler, fromUnlock); + return new WallpaperOpenLauncherAnimationRunner(fromUnlock); } /** @@ -1348,7 +1342,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener /** * Closing animator that animates the window into its final location on the workspace. */ - private RectFSpringAnim getClosingWindowAnimators(AnimatorSet animation, + protected RectFSpringAnim getClosingWindowAnimators(AnimatorSet animation, RemoteAnimationTarget[] targets, View launcherView, PointF velocityPxPerS, RectF closingWindowStartRect, float startWindowCornerRadius) { FloatingIconView floatingIconView = null; @@ -1473,11 +1467,11 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener float startShadowRadius = areAllTargetsTranslucent(appTargets) ? 0 : mMaxShadowRadius; closingAnimator.setDuration(duration); closingAnimator.addUpdateListener(new MultiValueUpdateListener() { - FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7); - FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7); + FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DECELERATE_1_7); + FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DECELERATE_1_7); FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR); FloatProp mShadowRadius = new FloatProp(startShadowRadius, 0, 0, duration, - DEACCEL_1_7); + DECELERATE_1_7); @Override public void onUpdate(float percent, boolean initOnly) { @@ -1588,89 +1582,80 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener RectF startRect, float startWindowCornerRadius, boolean fromPredictiveBack) { - AnimatorSet anim = null; + AnimatorSet anim = new AnimatorSet(); RectFSpringAnim rectFSpringAnim = null; - RemoteAnimationProvider provider = mRemoteAnimationProvider; - if (provider != null) { - anim = provider.createWindowAnimation(appTargets, wallpaperTargets); - } - - if (anim == null) { - anim = new AnimatorSet(); - - final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible() - || launcherIsATargetWithMode(appTargets, MODE_OPENING); - - View launcherView = findLauncherView(appTargets); - boolean playFallBackAnimation = (launcherView == null - && launcherIsForceInvisibleOrOpening) - || mLauncher.getWorkspace().isOverlayShown() - || shouldPlayFallbackClosingAnimation(appTargets); - - boolean playWorkspaceReveal = true; - boolean skipAllAppsScale = false; - if (fromUnlock) { - anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets)); - } else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get() - && !playFallBackAnimation) { - // Use a fixed velocity to start the animation. - float velocityPxPerS = DynamicResource.provider(mLauncher) - .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s); - PointF velocity = new PointF(0, -velocityPxPerS); - rectFSpringAnim = getClosingWindowAnimators( - anim, appTargets, launcherView, velocity, startRect, - startWindowCornerRadius); - if (mLauncher.isInState(LauncherState.ALL_APPS)) { - // Skip scaling all apps, otherwise FloatingIconView will get wrong - // layout bounds. - skipAllAppsScale = true; - } else if (!fromPredictiveBack) { - anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y, - true /* animateOverviewScrim */, launcherView).getAnimators()); - - if (!areAllTargetsTranslucent(appTargets)) { - anim.play(ObjectAnimator.ofFloat(mLauncher.getDepthController().stateDepth, - MULTI_PROPERTY_VALUE, - BACKGROUND_APP.getDepth(mLauncher), NORMAL.getDepth(mLauncher))); - } - - // We play StaggeredWorkspaceAnim as a part of the closing window animation. - playWorkspaceReveal = false; + final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible() + || launcherIsATargetWithMode(appTargets, MODE_OPENING); + + View launcherView = findLauncherView(appTargets); + boolean playFallBackAnimation = (launcherView == null + && launcherIsForceInvisibleOrOpening) + || mLauncher.getWorkspace().isOverlayShown() + || shouldPlayFallbackClosingAnimation(appTargets); + + boolean playWorkspaceReveal = true; + boolean skipAllAppsScale = false; + if (fromUnlock) { + anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets)); + } else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get() + && !playFallBackAnimation) { + // Use a fixed velocity to start the animation. + float velocityPxPerS = DynamicResource.provider(mLauncher) + .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s); + PointF velocity = new PointF(0, -velocityPxPerS); + rectFSpringAnim = getClosingWindowAnimators( + anim, appTargets, launcherView, velocity, startRect, + startWindowCornerRadius); + if (mLauncher.isInState(LauncherState.ALL_APPS)) { + // Skip scaling all apps, otherwise FloatingIconView will get wrong + // layout bounds. + skipAllAppsScale = true; + } else if (!fromPredictiveBack) { + anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y, + true /* animateOverviewScrim */, launcherView).getAnimators()); + + if (!areAllTargetsTranslucent(appTargets)) { + anim.play(ObjectAnimator.ofFloat(mLauncher.getDepthController().stateDepth, + MULTI_PROPERTY_VALUE, + BACKGROUND_APP.getDepth(mLauncher), NORMAL.getDepth(mLauncher))); } - } else { - anim.play(getFallbackClosingWindowAnimators(appTargets)); + + // We play StaggeredWorkspaceAnim as a part of the closing window animation. + playWorkspaceReveal = false; } + } else { + anim.play(getFallbackClosingWindowAnimators(appTargets)); + } - // Normally, we run the launcher content animation when we are transitioning - // home, but if home is already visible, then we don't want to animate the - // contents of launcher unless we know that we are animating home as a result - // of the home button press with quickstep, which will result in launcher being - // started on touch down, prior to the animation home (and won't be in the - // targets list because it is already visible). In that case, we force - // invisibility on touch down, and only reset it after the animation to home - // is initialized. - if (launcherIsForceInvisibleOrOpening) { - addCujInstrumentation( - anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME); - // Only register the content animation for cancellation when state changes - mLauncher.getStateManager().setCurrentAnimation(anim); - - if (mLauncher.isInState(LauncherState.ALL_APPS)) { - Pair<AnimatorSet, Runnable> contentAnimator = - getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY, - skipAllAppsScale); - anim.play(contentAnimator.first); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - contentAnimator.second.run(); - } - }); - } else { - if (playWorkspaceReveal) { - anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators()); + // Normally, we run the launcher content animation when we are transitioning + // home, but if home is already visible, then we don't want to animate the + // contents of launcher unless we know that we are animating home as a result + // of the home button press with quickstep, which will result in launcher being + // started on touch down, prior to the animation home (and won't be in the + // targets list because it is already visible). In that case, we force + // invisibility on touch down, and only reset it after the animation to home + // is initialized. + if (launcherIsForceInvisibleOrOpening) { + addCujInstrumentation(anim, playFallBackAnimation + ? CUJ_APP_CLOSE_TO_HOME_FALLBACK : CUJ_APP_CLOSE_TO_HOME); + // Only register the content animation for cancellation when state changes + mLauncher.getStateManager().setCurrentAnimation(anim); + + if (mLauncher.isInState(LauncherState.ALL_APPS)) { + Pair<AnimatorSet, Runnable> contentAnimator = + getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY, + skipAllAppsScale); + anim.play(contentAnimator.first); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + contentAnimator.second.run(); } + }); + } else { + if (playWorkspaceReveal) { + anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators()); } } } @@ -1683,11 +1668,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener */ protected class WallpaperOpenLauncherAnimationRunner implements RemoteAnimationFactory { - private final Handler mHandler; private final boolean mFromUnlock; - public WallpaperOpenLauncherAnimationRunner(Handler handler, boolean fromUnlock) { - mHandler = handler; + public WallpaperOpenLauncherAnimationRunner(boolean fromUnlock) { mFromUnlock = fromUnlock; } @@ -1795,7 +1778,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener @Nullable private static ContainerAnimationRunner from( View v, StartingWindowListener startingWindowListener, RunnableList onEndCallback) { - View viewToUse = findViewWithBackground(v); + View viewToUse = findLaunchableViewWithBackground(v); if (viewToUse == null) { viewToUse = v; } @@ -1833,19 +1816,23 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener new ActivityLaunchAnimator.AnimationDelegate(controller, callback, listener)); } - /** Finds the closest parent of [view] (inclusive) with a background drawable. */ + /** + * Finds the closest parent of [view] (inclusive) that implements {@link LaunchableView} and + * has a background drawable. + */ @Nullable - private static View findViewWithBackground(View view) { + private static <T extends View & LaunchableView> T findLaunchableViewWithBackground( + View view) { View current = view; - while (current.getBackground() == null) { + while (current.getBackground() == null || !(current instanceof LaunchableView)) { if (!(current.getParent() instanceof View)) { return null; } - current = (View) view.getParent(); + current = (View) current.getParent(); } - return current; + return (T) current; } @Override @@ -2043,7 +2030,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener if (progress >= end) { return 0f; } - return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5); + return Utilities.mapToRange(progress, start, end, 1, 0, ACCELERATE_1_5); } } diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java index 34316dbb1b..f82474a8e8 100644 --- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -36,13 +36,11 @@ import com.android.launcher3.Utilities; import com.android.launcher3.allapps.FloatingHeaderRow; import com.android.launcher3.allapps.FloatingHeaderView; import com.android.launcher3.anim.AlphaUpdateListener; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.keyboard.FocusIndicatorHelper; import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.WorkspaceItemInfo; -import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.views.ActivityContext; import java.util.ArrayList; @@ -65,7 +63,6 @@ public class PredictionRowView<T extends Context & ActivityContext> private FloatingHeaderView mParent; private boolean mPredictionsEnabled = false; - private OnLongClickListener mOnIconLongClickListener = ItemLongClickListener.INSTANCE_ALL_APPS; public PredictionRowView(@NonNull Context context) { this(context, null); @@ -106,22 +103,12 @@ public class PredictionRowView<T extends Context & ActivityContext> mActivityContext.getAppsView().getAppsStore().unregisterIconContainer(this); } } - - // Set the predicted row in All Apps' text line to 1. - if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() - || FeatureFlags.ENABLE_TWOLINE_DEVICESEARCH.get()) { - for (int i = 0; i < getChildCount(); i++) { - BubbleTextView icon = (BubbleTextView) getChildAt(i); - icon.setMaxLines(1); - } - } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(getExpectedHeight(), MeasureSpec.EXACTLY)); - updateVisibility(); } @Override @@ -185,15 +172,6 @@ public class PredictionRowView<T extends Context & ActivityContext> applyPredictionApps(); } - /** - * Sets the long click listener for predictions for any future predictions. - * - * Existing predictions in the container are not updated with this new callback. - */ - public void setOnIconLongClickListener(OnLongClickListener onIconLongClickListener) { - mOnIconLongClickListener = onIconLongClickListener; - } - @Override public void onDeviceProfileChanged(DeviceProfile dp) { mNumPredictedAppsPerRow = dp.numShownAllAppsColumns; @@ -209,9 +187,9 @@ public class PredictionRowView<T extends Context & ActivityContext> LayoutInflater inflater = mActivityContext.getAppsView().getLayoutInflater(); while (getChildCount() < mNumPredictedAppsPerRow) { BubbleTextView icon = (BubbleTextView) inflater.inflate( - R.layout.all_apps_icon, this, false); + R.layout.all_apps_prediction_row_icon, this, false); icon.setOnClickListener(mActivityContext.getItemOnClickListener()); - icon.setOnLongClickListener(mOnIconLongClickListener); + icon.setOnLongClickListener(mActivityContext.getAllAppsItemLongClickListener()); icon.setLongPressTimeoutFactor(1f); icon.setOnFocusChangeListener(mFocusHelper); diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java index 048243e345..b77c43fc2f 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java @@ -95,9 +95,7 @@ public class HotseatEduController { } } if (pageId == -1) { - pageId = LauncherSettings.Settings.call(mLauncher.getContentResolver(), - LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) - .getInt(LauncherSettings.Settings.EXTRA_VALUE); + pageId = mLauncher.getModel().getModelDbController().getNewScreenId(); mNewScreens = IntArray.wrap(pageId); } boolean isPortrait = !mLauncher.getDeviceProfile().isVerticalBarLayout(); diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java index 80bdb6f153..db225be4a9 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java @@ -19,7 +19,6 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_DENY; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_SEEN; -import android.animation.PropertyValuesHolder; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; @@ -37,7 +36,6 @@ import com.android.launcher3.Insettable; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.R; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.celllayout.CellLayoutLayoutParams; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.uioverrides.PredictedAppIcon; @@ -160,14 +158,11 @@ public class HotseatEduDialog extends AbstractSlideInView<Launcher> implements I } private void animateOpen() { - if (mIsOpen || mOpenCloseAnimator.isRunning()) { + if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) { return; } mIsOpen = true; - mOpenCloseAnimator.setValues( - PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); - mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - mOpenCloseAnimator.start(); + setUpDefaultOpenAnimation().start(); } @Override diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index 85d0ab5315..a63f9e8b29 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -22,11 +22,13 @@ import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettin import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.FlagDebugUtils.appendFlag; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.ComponentName; +import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.View; import android.view.ViewGroup; @@ -60,9 +62,11 @@ import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.views.Snackbar; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.StringJoiner; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -74,6 +78,7 @@ public class HotseatPredictionController implements DragController.DragListener, SystemShortcut.Factory<QuickstepLauncher>, DeviceProfile.OnDeviceProfileChangeListener, DragSource, ViewGroup.OnHierarchyChangeListener { + private static final String TAG = "HotseatPredictionController"; private static final int FLAG_UPDATE_PAUSED = 1 << 0; private static final int FLAG_DRAG_IN_PROGRESS = 1 << 1; private static final int FLAG_FILL_IN_PROGRESS = 1 << 2; @@ -114,8 +119,7 @@ public class HotseatPredictionController implements DragController.DragListener, WorkspaceItemInfo dragItem = new WorkspaceItemInfo((WorkspaceItemInfo) v.getTag()); v.setVisibility(View.INVISIBLE); mLauncher.getWorkspace().beginDragShared( - v, null, this, dragItem, new DragPreviewProvider(v), - mLauncher.getDefaultWorkspaceDragOptions()); + v, null, this, dragItem, new DragPreviewProvider(v), new DragOptions()); return true; }; @@ -184,6 +188,7 @@ public class HotseatPredictionController implements DragController.DragListener, } private void fillGapsWithPrediction(boolean animate) { + Log.d(TAG, "fillGapsWithPrediction flags: " + getStateString(mPauseFlags)); if (mPauseFlags != 0) { return; } @@ -208,12 +213,16 @@ public class HotseatPredictionController implements DragController.DragListener, View child = mHotseat.getChildAt( mHotseat.getCellXFromOrder(rank), mHotseat.getCellYFromOrder(rank)); + Log.d(TAG, "Hotseat app child is: " + child + " and isPredictedIcon() evaluates to" + + ": " + isPredictedIcon(child)); if (child != null && !isPredictedIcon(child)) { continue; } if (mPredictedItems.size() <= predictionIndex) { // Remove predicted apps from the past + Log.d(TAG, "Remove predicted apps from the past\nPrediction Index: " + + predictionIndex); if (isPredictedIcon(child)) { mHotseat.removeView(child); } @@ -221,6 +230,11 @@ public class HotseatPredictionController implements DragController.DragListener, } WorkspaceItemInfo predictedItem = (WorkspaceItemInfo) mPredictedItems.get(predictionIndex++); + Log.d(TAG, "Predicted item is: " + predictedItem); + if (child != null) { + Log.d(TAG, "Predicted item is enabled: " + child.isEnabled()); + } + if (isPredictedIcon(child) && child.isEnabled()) { PredictedAppIcon icon = (PredictedAppIcon) child; boolean animateIconChange = icon.shouldAnimateIconChange(predictedItem); @@ -240,6 +254,7 @@ public class HotseatPredictionController implements DragController.DragListener, } private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate) { + Log.d(TAG, "bindItems to hotseat: " + itemsToAdd); AnimatorSet animationSet = new AnimatorSet(); for (WorkspaceItemInfo item : itemsToAdd) { PredictedAppIcon icon = PredictedAppIcon.createIcon(mHotseat, item); @@ -279,6 +294,7 @@ public class HotseatPredictionController implements DragController.DragListener, * start and pauses predicted apps update on the hotseat */ public void setPauseUIUpdate(boolean paused) { + Log.d(TAG, "setPauseUIUpdate parameter `paused` is " + paused); mPauseFlags = paused ? (mPauseFlags | FLAG_UPDATE_PAUSED) : (mPauseFlags & ~FLAG_UPDATE_PAUSED); @@ -293,8 +309,10 @@ public class HotseatPredictionController implements DragController.DragListener, public void setPredictedItems(FixedContainerItems items) { mPredictedItems = new ArrayList(items.items); if (mPredictedItems.isEmpty()) { + Log.d(TAG, "Predicted items is initially empty"); HotseatRestoreHelper.restoreBackup(mLauncher); } + Log.d(TAG, "Predicted items: " + mPredictedItems); fillGapsWithPrediction(); } @@ -501,4 +519,21 @@ public class HotseatPredictionController implements DragController.DragListener, && ((WorkspaceItemInfo) view.getTag()).container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; } + + private static String getStateString(int flags) { + StringJoiner str = new StringJoiner("|"); + appendFlag(str, flags, FLAG_UPDATE_PAUSED, "FLAG_UPDATE_PAUSED"); + appendFlag(str, flags, FLAG_DRAG_IN_PROGRESS, "FLAG_DRAG_IN_PROGRESS"); + appendFlag(str, flags, FLAG_FILL_IN_PROGRESS, "FLAG_FILL_IN_PROGRESS"); + appendFlag(str, flags, FLAG_REMOVING_PREDICTED_ICON, + "FLAG_REMOVING_PREDICTED_ICON"); + return str.toString(); + } + + public void dump(String prefix, PrintWriter writer) { + writer.println(prefix + this.getClass().getSimpleName()); + writer.println(prefix + "\tFlags: " + getStateString(mPauseFlags)); + writer.println(prefix + "\tmHotSeatItemsCount: " + mHotSeatItemsCount); + writer.println(prefix + "\tmPredictedItems: " + mPredictedItems); + } } diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java index 726abff5da..8c01d04095 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java @@ -22,9 +22,10 @@ import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.content.Context; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherSettings; +import com.android.launcher3.LauncherModel; import com.android.launcher3.model.GridBackupTable; -import com.android.launcher3.provider.LauncherDbUtils; +import com.android.launcher3.model.ModelDbController; +import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; /** * A helper class to manage migration revert restoration for hybrid hotseat @@ -36,16 +37,13 @@ public class HotseatRestoreHelper { */ public static void createBackup(Context context) { MODEL_EXECUTOR.execute(() -> { - try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction) - LauncherSettings.Settings.call( - context.getContentResolver(), - LauncherSettings.Settings.METHOD_NEW_TRANSACTION) - .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) { + ModelDbController dbController = LauncherAppState.getInstance(context) + .getModel().getModelDbController(); + try (SQLiteTransaction transaction = dbController.newTransaction()) { GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb()); backupTable.createCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE); transaction.commit(); - LauncherSettings.Settings.call(context.getContentResolver(), - LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE); + dbController.refreshHotseatRestoreTable(); } }); } @@ -55,18 +53,15 @@ public class HotseatRestoreHelper { */ public static void restoreBackup(Context context) { MODEL_EXECUTOR.execute(() -> { - try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction) - LauncherSettings.Settings.call( - context.getContentResolver(), - LauncherSettings.Settings.METHOD_NEW_TRANSACTION) - .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) { + LauncherModel model = LauncherAppState.getInstance(context).getModel(); + try (SQLiteTransaction transaction = model.getModelDbController().newTransaction()) { if (!tableExists(transaction.getDb(), HYBRID_HOTSEAT_BACKUP_TABLE)) { return; } GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb()); backupTable.restoreFromCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE, true); transaction.commit(); - LauncherAppState.getInstance(context).getModel().forceReload(); + model.forceReload(); } }); } diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java index bc97df1fea..32361a862a 100644 --- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java +++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java @@ -52,6 +52,7 @@ import androidx.annotation.AnyThread; import androidx.annotation.CallSuper; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; import com.android.launcher3.InvariantDeviceProfile; @@ -93,11 +94,14 @@ public class QuickstepModelDelegate extends ModelDelegate { private static final boolean IS_DEBUG = false; private static final String TAG = "QuickstepModelDelegate"; - private final PredictorState mAllAppsState = + @VisibleForTesting + final PredictorState mAllAppsState = new PredictorState(CONTAINER_PREDICTION, "all_apps_predictions"); - private final PredictorState mHotseatState = + @VisibleForTesting + final PredictorState mHotseatState = new PredictorState(CONTAINER_HOTSEAT_PREDICTION, "hotseat_predictions"); - private final PredictorState mWidgetsRecommendationState = + @VisibleForTesting + final PredictorState mWidgetsRecommendationState = new PredictorState(CONTAINER_WIDGETS_PREDICTION, "widgets_prediction"); private final InvariantDeviceProfile mIDP; @@ -348,12 +352,7 @@ public class QuickstepModelDelegate extends ModelDelegate { .build())); // TODO: get bundle - registerPredictor(mHotseatState, apm.createAppPredictionSession( - new AppPredictionContext.Builder(context) - .setUiSurface("hotseat") - .setPredictedTargetCount(mIDP.numDatabaseHotseatIcons) - .setExtras(convertDataModelToAppTargetBundle(context, mDataModel)) - .build())); + registerHotseatPredictor(apm, context); registerWidgetsPredictor(apm.createAppPredictionSession( new AppPredictionContext.Builder(context) @@ -363,6 +362,29 @@ public class QuickstepModelDelegate extends ModelDelegate { .build())); } + @WorkerThread + private void recreateHotseatPredictor() { + mHotseatState.destroyPredictor(); + if (!mActive) { + return; + } + Context context = mApp.getContext(); + AppPredictionManager apm = context.getSystemService(AppPredictionManager.class); + if (apm == null) { + return; + } + registerHotseatPredictor(apm, context); + } + + private void registerHotseatPredictor(AppPredictionManager apm, Context context) { + registerPredictor(mHotseatState, apm.createAppPredictionSession( + new AppPredictionContext.Builder(context) + .setUiSurface("hotseat") + .setPredictedTargetCount(mIDP.numDatabaseHotseatIcons) + .setExtras(convertDataModelToAppTargetBundle(context, mDataModel)) + .build())); + } + private void registerPredictor(PredictorState state, AppPredictor predictor) { state.setTargets(Collections.emptyList()); state.predictor = predictor; @@ -393,7 +415,8 @@ public class QuickstepModelDelegate extends ModelDelegate { mWidgetsRecommendationState.predictor.requestPredictionUpdate(); } - private void onAppTargetEvent(AppTargetEvent event, int client) { + @VisibleForTesting + void onAppTargetEvent(AppTargetEvent event, int client) { PredictorState state; switch(client) { case CONTAINER_PREDICTION: @@ -411,6 +434,13 @@ public class QuickstepModelDelegate extends ModelDelegate { state.predictor.notifyAppTargetEvent(event); Log.d(TAG, "notifyAppTargetEvent action=" + event.getAction() + " launchLocation=" + event.getLaunchLocation()); + if (state == mHotseatState + && (event.getAction() == AppTargetEvent.ACTION_PIN + || event.getAction() == AppTargetEvent.ACTION_UNPIN)) { + // Recreate hot seat predictor when we need to query for hot seat due to pin or + // unpin app icons. + recreateHotseatPredictor(); + } } } diff --git a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java index 4d7cc85ed1..6d90b03533 100644 --- a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java +++ b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java @@ -24,6 +24,8 @@ import android.content.IntentSender.SendIntentException; import android.os.Bundle; import android.util.Log; +import com.android.launcher3.util.StartActivityParams; + public class ProxyActivityStarter extends Activity { private static final String TAG = "ProxyActivityStarter"; diff --git a/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java b/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java index 8720bd8f26..b982688ce7 100644 --- a/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java +++ b/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java @@ -18,9 +18,7 @@ package com.android.launcher3.secondarydisplay; import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT; import android.content.Context; -import android.view.View; -import com.android.launcher3.allapps.ActivityAllAppsContainerView; import com.android.launcher3.appprediction.AppsDividerView; import com.android.launcher3.appprediction.PredictionRowView; import com.android.launcher3.model.BgDataModel; @@ -56,12 +54,4 @@ public final class SecondaryDisplayPredictionsImpl extends SecondaryDisplayPredi .findFixedRowByType(PredictionRowView.class) .setPredictedApps(item.items); } - - @Override - public void setLongClickListener(ActivityAllAppsContainerView<?> appsView, - View.OnLongClickListener onIconLongClickListener) { - appsView.getFloatingHeaderView() - .findFixedRowByType(PredictionRowView.class) - .setOnIconLongClickListener(onIconLongClickListener); - } } diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java index 70aa5d77b8..882682dc4a 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java @@ -16,7 +16,7 @@ package com.android.launcher3.statehandlers; -import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH; import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER; import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE; @@ -193,5 +193,6 @@ public class DepthController extends BaseDepthController implements StateHandler writer.println(prefix + "\tmIgnoreStateChangesDuringMultiWindowAnimation=" + mIgnoreStateChangesDuringMultiWindowAnimation); writer.println(prefix + "\tmPauseBlurs=" + mPauseBlurs); + writer.println(prefix + "\tmWaitingOnSurfaceValidity=" + mWaitingOnSurfaceValidity); } } diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java index d087d39955..ecf483caa4 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java @@ -15,14 +15,22 @@ */ package com.android.launcher3.statehandlers; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; + import android.os.SystemProperties; import android.util.Log; import android.view.View; +import androidx.annotation.Nullable; + import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.uioverrides.QuickstepLauncher; +import com.android.quickstep.GestureState; +import com.android.quickstep.SystemUiProxy; +import com.android.quickstep.views.DesktopAppSelectView; +import com.android.wm.shell.desktopmode.IDesktopTaskListener; /** * Controls the visibility of the workspace and the resumed / paused state when desktop mode @@ -32,18 +40,69 @@ public class DesktopVisibilityController { private static final String TAG = "DesktopVisController"; private static final boolean DEBUG = false; - + private static final boolean IS_STASHING_ENABLED = SystemProperties.getBoolean( + "persist.wm.debug.desktop_stashing", false); private final Launcher mLauncher; private boolean mFreeformTasksVisible; private boolean mInOverviewState; private boolean mGestureInProgress; + @Nullable + private IDesktopTaskListener mDesktopTaskListener; + private DesktopAppSelectView mSelectAppToast; + public DesktopVisibilityController(Launcher launcher) { mLauncher = launcher; } /** + * Register a listener with System UI to receive updates about desktop tasks state + */ + public void registerSystemUiListener() { + mDesktopTaskListener = new IDesktopTaskListener.Stub() { + @Override + public void onVisibilityChanged(int displayId, boolean visible) { + MAIN_EXECUTOR.execute(() -> { + if (displayId == mLauncher.getDisplayId()) { + if (DEBUG) { + Log.d(TAG, "desktop visibility changed value=" + visible); + } + setFreeformTasksVisible(visible); + } + }); + } + + @Override + public void onStashedChanged(int displayId, boolean stashed) { + if (!IS_STASHING_ENABLED) { + return; + } + MAIN_EXECUTOR.execute(() -> { + if (displayId == mLauncher.getDisplayId()) { + if (DEBUG) { + Log.d(TAG, "desktop stashed changed value=" + stashed); + } + if (stashed) { + showSelectAppToast(); + } else { + hideSelectAppToast(); + } + } + }); + } + }; + SystemUiProxy.INSTANCE.get(mLauncher).setDesktopTaskListener(mDesktopTaskListener); + } + + /** + * Clear listener from System UI that was set with {@link #registerSystemUiListener()} + */ + public void unregisterSystemUiListener() { + SystemUiProxy.INSTANCE.get(mLauncher).setDesktopTaskListener(null); + } + + /** * Whether desktop mode is supported. */ private boolean isDesktopModeSupported() { @@ -68,6 +127,7 @@ public class DesktopVisibilityController { if (!isDesktopModeSupported()) { return; } + if (freeformTasksVisible != mFreeformTasksVisible) { mFreeformTasksVisible = freeformTasksVisible; if (mFreeformTasksVisible) { @@ -111,25 +171,54 @@ public class DesktopVisibilityController { /** * Whether recents gesture is currently in progress. */ - public boolean isGestureInProgress() { + public boolean isRecentsGestureInProgress() { return mGestureInProgress; } /** - * Sets whether recents gesture is in progress. + * Notify controller that recents gesture has started. */ - public void setGestureInProgress(boolean gestureInProgress) { - if (DEBUG) { - Log.d(TAG, "setGestureInProgress: inProgress=" + gestureInProgress); + public void setRecentsGestureStart() { + if (!isDesktopModeSupported()) { + return; } + setRecentsGestureInProgress(true); + } + + /** + * Notify controller that recents gesture finished with the given + * {@link com.android.quickstep.GestureState.GestureEndTarget} + */ + public void setRecentsGestureEnd(@Nullable GestureState.GestureEndTarget endTarget) { if (!isDesktopModeSupported()) { return; } + setRecentsGestureInProgress(false); + + if (endTarget == null) { + // Gesture did not result in a new end target. Ensure launchers gets paused again. + markLauncherPaused(); + } + } + + private void setRecentsGestureInProgress(boolean gestureInProgress) { + if (DEBUG) { + Log.d(TAG, "setGestureInProgress: inProgress=" + gestureInProgress); + } if (gestureInProgress != mGestureInProgress) { mGestureInProgress = gestureInProgress; } } + /** + * Handle launcher moving to home due to home gesture or home button press. + */ + public void onHomeActionTriggered() { + if (IS_STASHING_ENABLED && areFreeformTasksVisible()) { + SystemUiProxy.INSTANCE.get(mLauncher).stashDesktopApps(mLauncher.getDisplayId()); + } + } + private void setLauncherViewsVisibility(int visibility) { if (DEBUG) { Log.d(TAG, "setLauncherViewsVisibility: visibility=" + visibility); @@ -168,4 +257,28 @@ public class DesktopVisibilityController { activity.setResumed(); } } + + private void showSelectAppToast() { + if (mSelectAppToast != null) { + return; + } + if (DEBUG) { + Log.d(TAG, "show toast to select desktop apps"); + } + Runnable onCloseCallback = () -> { + SystemUiProxy.INSTANCE.get(mLauncher).hideStashedDesktopApps(mLauncher.getDisplayId()); + }; + mSelectAppToast = DesktopAppSelectView.show(mLauncher, onCloseCallback); + } + + private void hideSelectAppToast() { + if (mSelectAppToast == null) { + return; + } + if (DEBUG) { + Log.d(TAG, "hide toast to select desktop apps"); + } + mSelectAppToast.hide(); + mSelectAppToast = null; + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java index 7f655cf231..072fc3026e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java @@ -22,9 +22,13 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.R; +import com.android.launcher3.statehandlers.DesktopVisibilityController; import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext; +import com.android.quickstep.LauncherActivityInterface; import com.android.quickstep.RecentsModel; +import com.android.quickstep.util.DesktopTask; import com.android.quickstep.util.GroupTask; +import com.android.quickstep.views.DesktopTaskView; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -102,21 +106,74 @@ public final class KeyboardQuickSwitchController implements mQuickSwitchViewController = new KeyboardQuickSwitchViewController( mControllers, overlayContext, keyboardQuickSwitchView, mControllerCallbacks); + DesktopVisibilityController desktopController = + LauncherActivityInterface.INSTANCE.getDesktopVisibilityController(); + final boolean onDesktop = + DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED + && desktopController != null + && desktopController.areFreeformTasksVisible(); + if (mModel.isTaskListValid(mTaskListChangeId)) { - mQuickSwitchViewController.openQuickSwitchView( - mTasks, mNumHiddenTasks, /* updateTasks= */ false, currentFocusedIndex); + mQuickSwitchViewController.openQuickSwitchView(mTasks, + mNumHiddenTasks, /* updateTasks= */ false, currentFocusedIndex, onDesktop); return; } + mTaskListChangeId = mModel.getTasks((tasks) -> { - // Only store MAX_TASK tasks, from most to least recent - Collections.reverse(tasks); - mTasks = tasks.stream().limit(MAX_TASKS).collect(Collectors.toList()); - mNumHiddenTasks = Math.max(0, tasks.size() - MAX_TASKS); - mQuickSwitchViewController.openQuickSwitchView( - mTasks, mNumHiddenTasks, /* updateTasks= */ true, currentFocusedIndex); + if (onDesktop) { + processLoadedTasksOnDesktop(tasks); + } else { + processLoadedTasks(tasks); + } + mQuickSwitchViewController.openQuickSwitchView(mTasks, + mNumHiddenTasks, /* updateTasks= */ true, currentFocusedIndex, onDesktop); }); } + private void processLoadedTasks(ArrayList<GroupTask> tasks) { + // Only store MAX_TASK tasks, from most to least recent + Collections.reverse(tasks); + + // Hide all desktop tasks and show them on the hidden tile + int hiddenDesktopTasks = 0; + if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) { + DesktopTask desktopTask = findDesktopTask(tasks); + if (desktopTask != null) { + hiddenDesktopTasks = desktopTask.tasks.size(); + tasks = tasks.stream() + .filter(t -> !(t instanceof DesktopTask)) + .collect(Collectors.toCollection(ArrayList<GroupTask>::new)); + } + } + mTasks = tasks.stream() + .limit(MAX_TASKS) + .collect(Collectors.toList()); + mNumHiddenTasks = Math.max(0, tasks.size() - MAX_TASKS) + hiddenDesktopTasks; + } + + private void processLoadedTasksOnDesktop(ArrayList<GroupTask> tasks) { + // Find the single desktop task that contains a grouping of desktop tasks + DesktopTask desktopTask = findDesktopTask(tasks); + + if (desktopTask != null) { + mTasks = desktopTask.tasks.stream().map(GroupTask::new).collect(Collectors.toList()); + // All other tasks, apart from the grouped desktop task, are hidden + mNumHiddenTasks = Math.max(0, tasks.size() - 1); + } else { + // Desktop tasks were visible, but the recents entry is missing. Fall back to empty list + mTasks = Collections.emptyList(); + mNumHiddenTasks = tasks.size(); + } + } + + @Nullable + private DesktopTask findDesktopTask(ArrayList<GroupTask> tasks) { + return (DesktopTask) tasks.stream() + .filter(t -> t instanceof DesktopTask) + .findFirst() + .orElse(null); + } + void closeQuickSwitchView() { if (mQuickSwitchViewController == null) { return; @@ -169,7 +226,7 @@ public final class KeyboardQuickSwitchController implements class ControllerCallbacks { int getTaskCount() { - return mNumHiddenTasks == 0 ? mTasks.size() : MAX_TASKS + 1; + return mTasks.size() + (mNumHiddenTasks == 0 ? 0 : 1); } @Nullable diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java index 8a11b57c11..3e1a6ae9cb 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java @@ -80,7 +80,7 @@ public class KeyboardQuickSwitchTaskView extends ConstraintLayout { setWillNotDraw(false); mBorderColor = ta.getColor( - R.styleable.TaskView_borderColor, DEFAULT_BORDER_COLOR); + R.styleable.TaskView_focusBorderColor, DEFAULT_BORDER_COLOR); ta.recycle(); } diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java index 2cdfb18dc5..4e9e3019a9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java @@ -42,10 +42,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.constraintlayout.widget.ConstraintLayout; +import com.android.app.animation.Interpolators; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatedFloat; -import com.android.launcher3.anim.Interpolators; import com.android.quickstep.util.GroupTask; import java.util.HashMap; @@ -190,8 +190,12 @@ public class KeyboardQuickSwitchView extends ConstraintLayout { ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams( width, mTaskViewHeight); - lp.endToEnd = PARENT_ID; - lp.startToEnd = previousView.getId(); + if (previousView == null) { + lp.startToStart = PARENT_ID; + } else { + lp.endToEnd = PARENT_ID; + lp.startToEnd = previousView.getId(); + } lp.topToTop = PARENT_ID; lp.bottomToBottom = PARENT_ID; lp.setMarginEnd(mSpacing); diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java index 3230c661d5..a293f748b9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java @@ -27,6 +27,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext; import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer; +import com.android.quickstep.SystemUiProxy; import com.android.quickstep.util.GroupTask; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -52,6 +53,8 @@ public class KeyboardQuickSwitchViewController { private int mCurrentFocusIndex = -1; + private boolean mOnDesktop; + protected KeyboardQuickSwitchViewController( @NonNull TaskbarControllers controllers, @NonNull TaskbarOverlayContext overlayContext, @@ -71,10 +74,12 @@ public class KeyboardQuickSwitchViewController { @NonNull List<GroupTask> tasks, int numHiddenTasks, boolean updateTasks, - int currentFocusIndexOverride) { + int currentFocusIndexOverride, + boolean onDesktop) { TaskbarOverlayDragLayer dragLayer = mOverlayContext.getDragLayer(); dragLayer.addView(mKeyboardQuickSwitchView); dragLayer.runOnClickOnce(v -> closeQuickSwitchView(true)); + mOnDesktop = onDesktop; mKeyboardQuickSwitchView.applyLoadPlan( mOverlayContext, @@ -136,6 +141,10 @@ public class KeyboardQuickSwitchViewController { GroupTask task = mControllerCallbacks.getTaskAt(index); if (task == null) { return Math.max(0, index); + } else if (mOnDesktop) { + UI_HELPER_EXECUTOR.execute(() -> + SystemUiProxy.INSTANCE.get(mKeyboardQuickSwitchView.getContext()) + .showDesktopApp(task.task1.key.id)); } else if (task.task2 == null) { UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance().startActivityFromRecents( diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index abf49eb195..4e834ec3ef 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -317,6 +317,8 @@ public class LauncherTaskbarUIController extends TaskbarUIController { .getTaskbarNavButtonTranslationYForInAppDisplay() .updateValue(mLauncher.getDeviceProfile().getTaskbarOffsetY() * mTaskbarInAppDisplayProgress.value); + mControllers.navbarButtonsViewController + .getOnTaskbarBackgroundNavButtonColorOverride().updateValue(progress); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java index 9c463cb68c..fcd8c806e4 100644 --- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java @@ -88,6 +88,7 @@ import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton; import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory; import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter; import com.android.launcher3.util.DimensionUtils; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.TouchController; @@ -148,8 +149,8 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT // Used for IME+A11Y buttons private final ViewGroup mEndContextualContainer; private final ViewGroup mStartContextualContainer; - private final int mLightIconColor; - private final int mDarkIconColor; + private final int mLightIconColorOnHome; + private final int mDarkIconColorOnHome; /** Color to use for navigation bar buttons, if they are on on a Taskbar surface background. */ private final int mOnBackgroundIconColor; @@ -197,6 +198,7 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT this::onComputeInsetsForSeparateWindow; private final RecentsHitboxExtender mHitboxExtender = new RecentsHitboxExtender(); private ImageView mRecentsButton; + private DisplayController mDisplayController; public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) { mContext = context; @@ -205,9 +207,11 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT mEndContextualContainer = mNavButtonsView.findViewById(R.id.end_contextual_buttons); mStartContextualContainer = mNavButtonsView.findViewById(R.id.start_contextual_buttons); - mLightIconColor = context.getColor(R.color.taskbar_nav_icon_light_color); - mDarkIconColor = context.getColor(R.color.taskbar_nav_icon_dark_color); - mOnBackgroundIconColor = Utilities.isDarkTheme(context) ? mLightIconColor : mDarkIconColor; + mLightIconColorOnHome = context.getColor(R.color.taskbar_nav_icon_light_color_on_home); + mDarkIconColorOnHome = context.getColor(R.color.taskbar_nav_icon_dark_color_on_home); + mOnBackgroundIconColor = Utilities.isDarkTheme(context) + ? context.getColor(R.color.taskbar_nav_icon_light_color) + : context.getColor(R.color.taskbar_nav_icon_dark_color); } /** @@ -224,6 +228,8 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT TaskbarManager.isPhoneMode(deviceProfile)); mNavButtonsView.getLayoutParams().height = p.y; + mDisplayController = DisplayController.INSTANCE.get(mContext); + mIsImeRenderingNavButtons = InputMethodService.canImeRenderGesturalNavButtons() && mContext.imeDrawsImeNavBar(); if (!mIsImeRenderingNavButtons) { @@ -630,18 +636,20 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT private void updateNavButtonColor() { final ArgbEvaluator argbEvaluator = ArgbEvaluator.getInstance(); - final int sysUiNavButtonIconColor = (int) argbEvaluator.evaluate( + final int sysUiNavButtonIconColorOnHome = (int) argbEvaluator.evaluate( mTaskbarNavButtonDarkIntensity.value, - mLightIconColor, - mDarkIconColor); + mLightIconColorOnHome, + mDarkIconColorOnHome); + // Override the color from framework if nav buttons are over an opaque Taskbar surface. final int iconColor = (int) argbEvaluator.evaluate( mOnBackgroundNavButtonColorOverrideMultiplier.value * Math.max( mOnTaskbarBackgroundNavButtonColorOverride.value, mSlideInViewVisibleNavButtonColorOverride.value), - sysUiNavButtonIconColor, + sysUiNavButtonIconColorOnHome, mOnBackgroundIconColor); + for (ImageView button : mAllButtons) { button.setImageTintList(ColorStateList.valueOf(iconColor)); } @@ -723,15 +731,12 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT boolean isInKidsMode = mContext.isNavBarKidsModeActive(); if (TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) { - if (!isThreeButtonNav) { - return; - } - NavButtonLayoutter navButtonLayoutter = NavButtonLayoutFactory.Companion.getUiLayoutter( dp, mNavButtonsView, res, isInKidsMode, isInSetup, isThreeButtonNav, - TaskbarManager.isPhoneMode(dp)); + TaskbarManager.isPhoneMode(dp), mDisplayController.getInfo().rotation); navButtonLayoutter.layoutButtons(dp, isContextualButtonShowing()); + updateNavButtonColor(); return; } @@ -937,8 +942,6 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT pw.println(prefix + "NavbarButtonsViewController:"); pw.println(prefix + "\tmState=" + getStateString(mState)); - pw.println(prefix + "\tmLightIconColor=" + Integer.toHexString(mLightIconColor)); - pw.println(prefix + "\tmDarkIconColor=" + Integer.toHexString(mDarkIconColor)); pw.println(prefix + "\tmFloatingRotationButtonBounds=" + mFloatingRotationButtonBounds); pw.println(prefix + "\tmSysuiStateFlags=" + QuickStepContract.getSystemUiStateString( mSysuiStateFlags)); @@ -949,6 +952,14 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT + mTaskbarNavButtonTranslationYForInAppDisplay.value); pw.println(prefix + "\t\tmTaskbarNavButtonTranslationYForIme=" + mTaskbarNavButtonTranslationYForIme.value); + pw.println(prefix + "\t\tmTaskbarNavButtonDarkIntensity=" + + mTaskbarNavButtonDarkIntensity.value); + pw.println(prefix + "\t\tmSlideInViewVisibleNavButtonColorOverride=" + + mSlideInViewVisibleNavButtonColorOverride.value); + pw.println(prefix + "\t\tmOnTaskbarBackgroundNavButtonColorOverride=" + + mOnTaskbarBackgroundNavButtonColorOverride.value); + pw.println(prefix + "\t\tmOnBackgroundNavButtonColorOverrideMultiplier=" + + mOnBackgroundNavButtonColorOverrideMultiplier.value); } private static String getStateString(int flags) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index a1390aeabb..b15dda2de1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -49,14 +49,15 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Process; -import android.os.SystemProperties; import android.os.Trace; import android.provider.Settings; import android.util.Log; import android.view.Display; import android.view.Gravity; import android.view.RoundedCorner; +import android.view.Surface; import android.view.View; +import android.view.WindowInsets; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.Toast; @@ -90,6 +91,8 @@ import com.android.launcher3.taskbar.bubbles.BubbleBarController; import com.android.launcher3.taskbar.bubbles.BubbleBarView; import com.android.launcher3.taskbar.bubbles.BubbleBarViewController; import com.android.launcher3.taskbar.bubbles.BubbleControllers; +import com.android.launcher3.taskbar.bubbles.BubbleDismissController; +import com.android.launcher3.taskbar.bubbles.BubbleDragController; import com.android.launcher3.taskbar.bubbles.BubbleStashController; import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController; import com.android.launcher3.taskbar.overlay.TaskbarOverlayController; @@ -117,6 +120,7 @@ import com.android.systemui.unfold.updates.RotationChangeProvider; import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider; import java.io.PrintWriter; +import java.util.Collections; import java.util.Optional; /** @@ -128,8 +132,6 @@ public class TaskbarActivityContext extends BaseTaskbarContext { private static final String IME_DRAWS_IME_NAV_BAR_RES_NAME = "config_imeDrawsImeNavBar"; - private static final boolean ENABLE_THREE_BUTTON_TASKBAR = - SystemProperties.getBoolean("persist.debug.taskbar_three_button", false); private static final String TAG = "TaskbarActivityContext"; private static final String WINDOW_TITLE = "Taskbar"; @@ -146,7 +148,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { private int mLastRequestedNonFullscreenHeight; private NavigationMode mNavMode; - private final boolean mImeDrawsImeNavBar; + private boolean mImeDrawsImeNavBar; private final ViewCache mViewCache = new ViewCache(); private final boolean mIsSafeModeEnabled; @@ -169,30 +171,27 @@ public class TaskbarActivityContext extends BaseTaskbarContext { TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider unfoldTransitionProgressProvider) { super(windowContext); - final Resources resources = getResources(); - matchDeviceProfile(launcherDp, getResources()); + applyDeviceProfile(launcherDp); + + final Resources resources = getResources(); - mNavMode = DisplayController.getNavigationMode(windowContext); mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false); mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode", () -> getPackageManager().isSafeMode()); + + // TODO(b/244231596) For shared Taskbar window, update this value in applyDeviceProfile() + // instead so to get correct value when recreating the taskbar SettingsCache settingsCache = SettingsCache.INSTANCE.get(this); mIsUserSetupComplete = settingsCache.getValue( Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0); - mIsNavBarForceVisible = settingsCache.getValue( - Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0); - - // TODO(b/244231596) For shared Taskbar window, update this value in init() instead so - // to get correct value when recreating the taskbar mIsNavBarKidsMode = settingsCache.getValue( Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0); + mIsNavBarForceVisible = mIsNavBarKidsMode; // Get display and corners first, as views might use them in constructor. Display display = windowContext.getDisplay(); - Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY - ? windowContext.getApplicationContext() - : windowContext.getApplicationContext().createDisplayContext(display); + Context c = getApplicationContext(); mWindowManager = c.getSystemService(WindowManager.class); mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT); mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT); @@ -216,29 +215,34 @@ public class TaskbarActivityContext extends BaseTaskbarContext { // If Bubble bar is present, TaskbarControllers depends on it so build it first. Optional<BubbleControllers> bubbleControllersOptional = Optional.empty(); - if (BubbleBarController.BUBBLE_BAR_ENABLED) { + if (BubbleBarController.BUBBLE_BAR_ENABLED && bubbleBarView != null) { bubbleControllersOptional = Optional.of(new BubbleControllers( new BubbleBarController(this, bubbleBarView), new BubbleBarViewController(this, bubbleBarView), new BubbleStashController(this), - new BubbleStashedHandleViewController(this, bubbleHandleView))); + new BubbleStashedHandleViewController(this, bubbleHandleView), + new BubbleDragController(this), + new BubbleDismissController(this, mDragLayer))); } // Construct controllers. + RotationButtonController rotationButtonController = new RotationButtonController(this, + c.getColor(R.color.floating_rotation_button_light_color), + c.getColor(R.color.floating_rotation_button_dark_color), + R.drawable.ic_sysbar_rotate_button_ccw_start_0, + R.drawable.ic_sysbar_rotate_button_ccw_start_90, + R.drawable.ic_sysbar_rotate_button_cw_start_0, + R.drawable.ic_sysbar_rotate_button_cw_start_90, + () -> getDisplay().getRotation()); + rotationButtonController.setBgExecutor(Executors.THREAD_POOL_EXECUTOR); + mControllers = new TaskbarControllers(this, new TaskbarDragController(this), buttonController, isDesktopMode ? new DesktopNavbarButtonsViewController(this, navButtonsView) : new NavbarButtonsViewController(this, navButtonsView), - new RotationButtonController(this, - c.getColor(R.color.floating_rotation_button_light_color), - c.getColor(R.color.floating_rotation_button_dark_color), - R.drawable.ic_sysbar_rotate_button_ccw_start_0, - R.drawable.ic_sysbar_rotate_button_ccw_start_90, - R.drawable.ic_sysbar_rotate_button_cw_start_0, - R.drawable.ic_sysbar_rotate_button_cw_start_90, - () -> getDisplay().getRotation()), + rotationButtonController, new TaskbarDragLayerController(this, mDragLayer), new TaskbarViewController(this, taskbarView), new TaskbarScrimViewController(this, taskbarScrimView), @@ -267,13 +271,48 @@ public class TaskbarActivityContext extends BaseTaskbarContext { bubbleControllersOptional); } + /** Updates {@link DeviceProfile} instances for any Taskbar windows. */ + public void updateDeviceProfile(DeviceProfile launcherDp) { + applyDeviceProfile(launcherDp); + + mControllers.taskbarOverlayController.updateLauncherDeviceProfile(launcherDp); + AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE); + // Reapply fullscreen to take potential new screen size into account. + setTaskbarWindowFullscreen(mIsFullscreen); + + dispatchDeviceProfileChanged(); + } + + /** + * Copy the original DeviceProfile, match the number of hotseat icons and qsb width and update + * the icon size + */ + private void applyDeviceProfile(DeviceProfile originDeviceProfile) { + mDeviceProfile = originDeviceProfile.toBuilder(this) + .withDimensionsOverride(deviceProfile -> { + // Taskbar should match the number of icons of hotseat + deviceProfile.numShownHotseatIcons = originDeviceProfile.numShownHotseatIcons; + // Same QSB width to have a smooth animation + deviceProfile.hotseatQsbWidth = originDeviceProfile.hotseatQsbWidth; + + // Update icon size + deviceProfile.iconSizePx = deviceProfile.taskbarIconSize; + deviceProfile.updateIconSize(1f, getResources()); + }).build(); + mNavMode = DisplayController.getNavigationMode(this); + } + + public void init(@NonNull TaskbarSharedState sharedState) { + mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, getResources(), false); mLastRequestedNonFullscreenHeight = getDefaultTaskbarWindowHeight(); - mWindowLayoutParams = - createDefaultWindowLayoutParams(TYPE_NAVIGATION_BAR_PANEL, WINDOW_TITLE); + mWindowLayoutParams = createAllWindowParams(); // Initialize controllers after all are constructed. mControllers.init(sharedState); + // This may not be necessary and can be reverted once we move towards recreating all + // controllers without re-creating the window + mControllers.rotationButtonController.onNavigationModeChanged(mNavMode.resValue); updateSysuiStateFlags(sharedState.sysuiStateFlags, true /* fromInit */); disableNavBarElements(sharedState.disableNavBarDisplayId, sharedState.disableNavBarState1, sharedState.disableNavBarState2, false /* animate */); @@ -303,24 +342,16 @@ public class TaskbarActivityContext extends BaseTaskbarContext { mControllers.taskbarStashController.showTaskbarFromBroadcast(); } + /** Toggles Taskbar All Apps overlay. */ + public void toggleAllApps() { + mControllers.taskbarAllAppsController.toggle(); + } + @Override public DeviceProfile getDeviceProfile() { return mDeviceProfile; } - /** Updates {@link DeviceProfile} instances for any Taskbar windows. */ - public void updateDeviceProfile(DeviceProfile launcherDp, NavigationMode navMode) { - mNavMode = navMode; - mControllers.taskbarOverlayController.updateLauncherDeviceProfile(launcherDp); - matchDeviceProfile(launcherDp, getResources()); - - AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE); - // Reapply fullscreen to take potential new screen size into account. - setTaskbarWindowFullscreen(mIsFullscreen); - - dispatchDeviceProfileChanged(); - } - @Override public void dispatchDeviceProfileChanged() { super.dispatchDeviceProfileChanged(); @@ -329,24 +360,6 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } /** - * Copy the original DeviceProfile, match the number of hotseat icons and qsb width and update - * the icon size - */ - private void matchDeviceProfile(DeviceProfile originDeviceProfile, Resources resources) { - mDeviceProfile = originDeviceProfile.toBuilder(this) - .withDimensionsOverride(deviceProfile -> { - // Taskbar should match the number of icons of hotseat - deviceProfile.numShownHotseatIcons = originDeviceProfile.numShownHotseatIcons; - // Same QSB width to have a smooth animation - deviceProfile.hotseatQsbWidth = originDeviceProfile.hotseatQsbWidth; - - // Update icon size - deviceProfile.iconSizePx = deviceProfile.taskbarIconSize; - deviceProfile.updateIconSize(1f, resources); - }).build(); - } - - /** * Returns the View bounds of transient taskbar. */ public Rect getTransientTaskbarBounds() { @@ -365,11 +378,6 @@ public class TaskbarActivityContext extends BaseTaskbarContext { * @param title The window title to pass to the created WindowManager.LayoutParams. */ public WindowManager.LayoutParams createDefaultWindowLayoutParams(int type, String title) { - DeviceProfile deviceProfile = getDeviceProfile(); - // Taskbar is on the logical bottom of the screen - boolean isVerticalBarLayout = TaskbarManager.isPhoneButtonNavMode(this) && - deviceProfile.isLandscape; - int windowFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SLIPPERY | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; @@ -378,17 +386,14 @@ public class TaskbarActivityContext extends BaseTaskbarContext { | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; } WindowManager.LayoutParams windowLayoutParams = new WindowManager.LayoutParams( - isVerticalBarLayout ? mLastRequestedNonFullscreenHeight : MATCH_PARENT, - isVerticalBarLayout ? MATCH_PARENT : mLastRequestedNonFullscreenHeight, + MATCH_PARENT, + mLastRequestedNonFullscreenHeight, type, windowFlags, PixelFormat.TRANSLUCENT); windowLayoutParams.setTitle(title); windowLayoutParams.packageName = getPackageName(); - windowLayoutParams.gravity = !isVerticalBarLayout ? - Gravity.BOTTOM : - Gravity.END; // TODO(b/230394142): seascape - + windowLayoutParams.gravity = Gravity.BOTTOM; windowLayoutParams.setFitInsetsTypes(0); windowLayoutParams.receiveInsetsIgnoringZOrder = true; windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; @@ -399,6 +404,64 @@ public class TaskbarActivityContext extends BaseTaskbarContext { TaskbarManager.isPhoneMode(mDeviceProfile) ? R.string.taskbar_phone_a11y_title : R.string.taskbar_a11y_title); + + return windowLayoutParams; + } + + /** + * Creates {@link WindowManager.LayoutParams} for Taskbar, and also sets LP.paramsForRotation + * for taskbar showing as navigation bar + */ + private WindowManager.LayoutParams createAllWindowParams() { + WindowManager.LayoutParams windowLayoutParams = + createDefaultWindowLayoutParams(TYPE_NAVIGATION_BAR_PANEL, + TaskbarActivityContext.WINDOW_TITLE); + boolean isPhoneNavMode = TaskbarManager.isPhoneButtonNavMode(this); + if (!isPhoneNavMode) { + return windowLayoutParams; + } + + // Provide WM layout params for all rotations to cache, see NavigationBar#getBarLayoutParams + int width = WindowManager.LayoutParams.MATCH_PARENT; + int height = WindowManager.LayoutParams.MATCH_PARENT; + int gravity = Gravity.BOTTOM; + windowLayoutParams.paramsForRotation = new WindowManager.LayoutParams[4]; + for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) { + WindowManager.LayoutParams lp = + createDefaultWindowLayoutParams(TYPE_NAVIGATION_BAR_PANEL, + TaskbarActivityContext.WINDOW_TITLE); + switch (rot) { + case Surface.ROTATION_0, Surface.ROTATION_180 -> { + // Defaults are fine + width = WindowManager.LayoutParams.MATCH_PARENT; + height = mLastRequestedNonFullscreenHeight; + gravity = Gravity.BOTTOM; + } + case Surface.ROTATION_90 -> { + width = mLastRequestedNonFullscreenHeight; + height = WindowManager.LayoutParams.MATCH_PARENT; + gravity = Gravity.END; + } + case Surface.ROTATION_270 -> { + width = mLastRequestedNonFullscreenHeight; + height = WindowManager.LayoutParams.MATCH_PARENT; + gravity = Gravity.START; + } + + } + lp.width = width; + lp.height = height; + lp.gravity = gravity; + windowLayoutParams.paramsForRotation[rot] = lp; + } + + // Override current layout params + WindowManager.LayoutParams currentParams = + windowLayoutParams.paramsForRotation[getDisplay().getRotation()]; + windowLayoutParams.width = currentParams.width; + windowLayoutParams.height = currentParams.height; + windowLayoutParams.gravity = currentParams.gravity; + return windowLayoutParams; } @@ -448,6 +511,11 @@ public class TaskbarActivityContext extends BaseTaskbarContext { return mControllers.taskbarDragController; } + @Nullable + public BubbleControllers getBubbleControllers() { + return mControllers.bubbleControllers.orElse(null); + } + @Override public ViewCache getViewCache() { return mViewCache; @@ -579,6 +647,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { Executors.MAIN_EXECUTOR.getHandler(), null, elapsedRealTime -> callbacks.executeAllAndDestroy()); options.setSplashScreenStyle(splashScreenStyle); + options.setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); return new ActivityOptionsWrapper(options, callbacks); } @@ -638,8 +708,12 @@ public class TaskbarActivityContext extends BaseTaskbarContext { mControllers.taskbarForceVisibleImmersiveController.updateSysuiFlags(systemUiStateFlags); mControllers.voiceInteractionWindowController.setIsVoiceInteractionWindowVisible( (systemUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0, fromInit); - mControllers.uiController.updateStateForSysuiFlags(systemUiStateFlags); + mControllers.bubbleControllers.ifPresent(controllers -> { + controllers.bubbleBarController.updateStateForSysuiFlags(systemUiStateFlags); + controllers.bubbleStashedHandleViewController.setIsHomeButtonDisabled( + mControllers.navbarButtonsViewController.isHomeDisabled()); + }); } /** @@ -735,7 +809,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } } mWindowLayoutParams.height = height; - mControllers.taskbarInsetsController.onTaskbarWindowHeightOrInsetsChanged(); + mControllers.taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged(); mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams); } @@ -783,6 +857,21 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } /** + * Applies forcibly show flag to taskbar window iff transient taskbar is unstashed. + */ + public void applyForciblyShownFlagWhileTransientTaskbarUnstashed(boolean shouldForceShow) { + if (!DisplayController.isTransientTaskbar(this)) { + return; + } + if (shouldForceShow) { + mWindowLayoutParams.forciblyShownTypes |= WindowInsets.Type.navigationBars(); + } else { + mWindowLayoutParams.forciblyShownTypes &= ~WindowInsets.Type.navigationBars(); + } + notifyUpdateLayoutParams(); + } + + /** * Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar * window. If we're now focusable, also move nav buttons to a separate window above IME. */ @@ -888,8 +977,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } } catch (NullPointerException - | ActivityNotFoundException - | SecurityException e) { + | ActivityNotFoundException + | SecurityException e) { Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT) .show(); Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e); @@ -936,9 +1025,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext { if (recents == null) { return; } - recents.getSplitSelectController().findLastActiveTaskAndRunCallback( - info.getComponentKey(), - foundTask -> { + recents.getSplitSelectController().findLastActiveTasksAndRunCallback( + Collections.singletonList(info.getComponentKey()), + foundTasks -> { + @Nullable Task foundTask = foundTasks.get(0); if (foundTask != null) { TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id); @@ -982,6 +1072,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { /** * Called when we detect a long press in the nav region before passing the gesture slop. + * * @return Whether taskbar handled the long press, and thus should cancel the gesture. */ public boolean onLongPressToUnstashTaskbar() { @@ -992,10 +1083,19 @@ public class TaskbarActivityContext extends BaseTaskbarContext { * Called when we want to unstash taskbar when user performs swipes up gesture. */ public void onSwipeToUnstashTaskbar() { - mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false); + mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(/* stash= */ false); mControllers.taskbarEduTooltipController.hide(); } + /** + * Called when we want to open bubblebar when user performs swipes up gesture. + */ + public void onSwipeToOpenBubblebar() { + mControllers.bubbleControllers.ifPresent(controllers -> { + controllers.bubbleStashController.showBubbleBar(/* expandBubbles= */ true); + }); + } + /** Returns {@code true} if Taskbar All Apps is open. */ public boolean isTaskbarAllAppsOpen() { return mControllers.taskbarAllAppsController.isOpen(); @@ -1041,7 +1141,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { * Called when we detect a motion down or up/cancel in the nav region while stashed. * * @param animateForward Whether to animate towards the unstashed hint state or back to stashed. - * @param forceUnstash Whether we force the unstash hint. + * @param forceUnstash Whether we force the unstash hint. */ public void startTaskbarUnstashHint(boolean animateForward, boolean forceUnstash) { // TODO(b/270395798): Clean up forceUnstash after removing long-press unstashing code. diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java index 70999e7131..8ab2ffadcf 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java @@ -45,6 +45,8 @@ public class TaskbarAutohideSuspendController implements public static final int FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER = 1 << 4; // Transient Taskbar is temporarily unstashed (pending a timeout). public static final int FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR = 1 << 5; + // User has hovered the taskbar. + public static final int FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS = 1 << 6; @IntDef(flag = true, value = { FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, @@ -53,6 +55,7 @@ public class TaskbarAutohideSuspendController implements FLAG_AUTOHIDE_SUSPEND_EDU_OPEN, FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER, FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR, + FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, }) @Retention(RetentionPolicy.SOURCE) public @interface AutohideSuspendFlag {} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt index ca2d1f82cd..d237c1f997 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt @@ -22,12 +22,12 @@ import android.graphics.Color import android.graphics.Paint import android.graphics.Path import android.graphics.RectF +import com.android.app.animation.Interpolators import com.android.launcher3.DeviceProfile import com.android.launcher3.R import com.android.launcher3.Utilities import com.android.launcher3.Utilities.mapRange import com.android.launcher3.Utilities.mapToRange -import com.android.launcher3.anim.Interpolators import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound import com.android.launcher3.util.DisplayController diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java index d3f80e31cd..d82f50116d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java @@ -239,6 +239,7 @@ public class TaskbarControllers { taskbarPopupController.onDestroy(); taskbarForceVisibleImmersiveController.onDestroy(); taskbarOverlayController.onDestroy(); + taskbarAllAppsController.onDestroy(); navButtonController.onDestroy(); taskbarInsetsController.onDestroy(); voiceInteractionWindowController.onDestroy(); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt index 17d10d3574..e215bc9d46 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt @@ -17,7 +17,6 @@ package com.android.launcher3.taskbar import android.annotation.SuppressLint import android.content.Context -import android.content.Intent import android.graphics.Rect import android.graphics.drawable.GradientDrawable import android.util.AttributeSet @@ -44,9 +43,6 @@ constructor( companion object { private const val TAG = "TaskbarDividerPopupView" private const val DIVIDER_POPUP_CLOSING_DELAY = 500L - private const val SETTINGS_PACKAGE_NAME = "com.android.settings" - private const val CHANGE_NAVIGATION_MODE_ACTION = - "com.android.settings.NAVIGATION_MODE_SETTINGS" @JvmStatic fun createAndPopulate( @@ -103,21 +99,12 @@ constructor( super.onFinishInflate() val taskbarSwitchOption = requireViewById<LinearLayout>(R.id.taskbar_switch_option) val alwaysShowTaskbarSwitch = requireViewById<Switch>(R.id.taskbar_pinning_switch) - val navigationModeChangeOption = - requireViewById<LinearLayout>(R.id.navigation_mode_switch_option) alwaysShowTaskbarSwitch.isChecked = alwaysShowTaskbarOn taskbarSwitchOption.setOnClickListener { alwaysShowTaskbarSwitch.isClickable = true alwaysShowTaskbarSwitch.isChecked = !alwaysShowTaskbarOn onClickAlwaysShowTaskbarSwitchOption() } - navigationModeChangeOption.setOnClickListener { - context.startActivity( - Intent(CHANGE_NAVIGATION_MODE_ACTION) - .setPackage(SETTINGS_PACKAGE_NAME) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - ) - } } /** Orient object as usual and then center object horizontally. */ diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java index 040b8f7bfe..3c7196a36f 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -15,16 +15,18 @@ */ package com.android.launcher3.taskbar; +import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; +import android.app.PendingIntent; import android.content.ClipData; import android.content.ClipDescription; import android.content.Intent; @@ -47,6 +49,7 @@ import android.window.SurfaceSyncGroup; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.internal.logging.InstanceId; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BubbleTextView; @@ -55,7 +58,6 @@ import com.android.launcher3.DropTarget; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.accessibility.DragViewStateAnnouncer; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragDriver; import com.android.launcher3.dragndrop.DragOptions; @@ -73,6 +75,7 @@ import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.ItemInfoMatcher; +import com.android.launcher3.views.BubbleTextHolder; import com.android.quickstep.util.LogUtils; import com.android.quickstep.util.MultiValueUpdateListener; import com.android.systemui.shared.recents.model.Task; @@ -149,6 +152,9 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im View view, @Nullable DragPreviewProvider dragPreviewProvider, @Nullable Point iconShift) { + if (view instanceof BubbleTextHolder) { + view = ((BubbleTextHolder) view).getBubbleText(); + } if (!(view instanceof BubbleTextView) || mDisallowLongClick) { return false; } @@ -193,13 +199,21 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im dragLayerY += dragRect.top; DragOptions dragOptions = new DragOptions(); - dragOptions.preDragCondition = null; - PopupContainerWithArrow<BaseTaskbarContext> popupContainer = - mControllers.taskbarPopupController.showForIcon(btv); - if (popupContainer != null) { - dragOptions.preDragCondition = popupContainer.createPreDragCondition(false); + // First, see if view is a search result that needs custom pre-drag conditions. + dragOptions.preDragCondition = + mControllers.taskbarAllAppsController.createPreDragConditionForSearch(btv); + + if (dragOptions.preDragCondition == null) { + // See if view supports a popup container. + PopupContainerWithArrow<BaseTaskbarContext> popupContainer = + mControllers.taskbarPopupController.showForIcon(btv); + if (popupContainer != null) { + dragOptions.preDragCondition = popupContainer.createPreDragCondition(false); + } } + if (dragOptions.preDragCondition == null) { + // Fallback pre-drag condition. dragOptions.preDragCondition = new DragOptions.PreDragCondition() { private DragView mDragView; @@ -213,13 +227,8 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im mDragView = dragObject.dragView; if (!shouldStartDrag(0)) { - mDragView.setOnAnimationEndCallback(() -> { - // Drag might be cancelled during the DragView animation, so check - // mIsPreDrag again. - if (mIsInPreDrag) { - callOnDragStart(); - } - }); + mDragView.setOnScaleAnimEndCallback( + TaskbarDragController.this::onPreDragAnimationEnd); } } @@ -230,12 +239,13 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im }; } + Point dragOffset = dragOptions.preDragCondition.getDragOffset(); return startDrag( drawable, /* view = */ null, /* originalView = */ btv, - dragLayerX, - dragLayerY, + dragLayerX + dragOffset.x, + dragLayerY + dragOffset.y, (View target, DropTarget.DragObject d, boolean success) -> {} /* DragSource */, (ItemInfo) btv.getTag(), dragRect, @@ -290,6 +300,11 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im mDragObject.dragInfo = dragInfo; mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy(); + if (mOptions.preDragCondition != null) { + dragView.setHasDragOffset(mOptions.preDragCondition.getDragOffset().x != 0 + || mOptions.preDragCondition.getDragOffset().y != 0); + } + if (dragRegion != null) { dragView.setDragRegion(new Rect(dragRegion)); } @@ -308,6 +323,14 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im return dragView; } + /** Invoked when an animation running as part of pre-drag finishes. */ + public void onPreDragAnimationEnd() { + // Drag might be cancelled during the DragView animation, so check mIsPreDrag again. + if (mIsInPreDrag) { + callOnDragStart(); + } + } + @Override protected void callOnDragStart() { super.callOnDragStart(); @@ -383,6 +406,17 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im item.user)); intent.putExtra(Intent.EXTRA_PACKAGE_NAME, item.getIntent().getPackage()); intent.putExtra(Intent.EXTRA_SHORTCUT_ID, deepShortcutId); + } else if (item.itemType == ITEM_TYPE_SEARCH_ACTION) { + // TODO(b/289261756): Buggy behavior when split opposite to an existing search pane. + intent.putExtra( + ClipDescription.EXTRA_PENDING_INTENT, + PendingIntent.getActivityAsUser( + mActivity, + /* requestCode= */ 0, + item.getIntent(), + PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, + /* options= */ null, + item.user)); } else { intent.putExtra(ClipDescription.EXTRA_PENDING_INTENT, launcherApps.getMainActivityLaunchIntent(item.getIntent().getComponent(), @@ -642,7 +676,7 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im final FloatProp mScale = new FloatProp(1f, toScale, 0, ANIM_DURATION_RETURN_ICON_TO_TASKBAR, FAST_OUT_SLOW_IN); final FloatProp mAlpha = new FloatProp(1f, toAlpha, 0, - ANIM_DURATION_RETURN_ICON_TO_TASKBAR, Interpolators.ACCEL_2); + ANIM_DURATION_RETURN_ICON_TO_TASKBAR, Interpolators.ACCELERATE_2); @Override public void onUpdate(float percent, boolean initOnly) { animListener.updateDragShadow(mDx.value, mDy.value, mScale.value, mAlpha.value); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java index f6de92618f..e521154497 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java @@ -21,6 +21,7 @@ import static android.view.KeyEvent.KEYCODE_BACK; import android.content.Context; import android.graphics.Canvas; import android.graphics.RectF; +import android.media.permission.SafeCloseable; import android.util.AttributeSet; import android.util.FloatProperty; import android.view.KeyEvent; @@ -31,6 +32,7 @@ import android.view.ViewTreeObserver; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.app.viewcapture.SettingsAwareViewCapture; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; @@ -68,6 +70,7 @@ public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> { // Initialized in init. private TaskbarDragLayerController.TaskbarDragLayerCallbacks mControllerCallbacks; + private SafeCloseable mViewCaptureCloseable; private float mTaskbarBackgroundOffset; @@ -128,12 +131,14 @@ public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> { protected void onAttachedToWindow() { super.onAttachedToWindow(); getViewTreeObserver().addOnComputeInternalInsetsListener(mTaskbarInsetsComputer); + mViewCaptureCloseable = SettingsAwareViewCapture.getInstance(getContext()) + .startCapture(getRootView(), ".Taskbar"); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - + mViewCaptureCloseable.close(); onDestroy(true); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java new file mode 100644 index 0000000000..c3ec1e5ad0 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java @@ -0,0 +1,177 @@ +/* + * 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.launcher3.taskbar; + +import static android.view.MotionEvent.ACTION_HOVER_ENTER; +import static android.view.MotionEvent.ACTION_HOVER_EXIT; +import static android.view.View.ALPHA; +import static android.view.View.SCALE_Y; +import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT; + +import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_EXCEPT_ON_BOARD_POPUP; +import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS; +import static com.android.launcher3.views.ArrowTipView.TEXT_ALPHA; + +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.view.ContextThemeWrapper; +import android.view.MotionEvent; +import android.view.View; + +import androidx.annotation.VisibleForTesting; + +import com.android.app.animation.Interpolators; +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.AccessibilityManagerCompat; +import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.views.ArrowTipView; + +/** + * Controls showing a tooltip in the taskbar above each icon when it is hovered. + */ +public class TaskbarHoverToolTipController implements View.OnHoverListener { + + @VisibleForTesting protected static final int HOVER_TOOL_TIP_REVEAL_START_DELAY = 400; + private static final int HOVER_TOOL_TIP_REVEAL_DURATION = 300; + private static final int HOVER_TOOL_TIP_EXIT_DURATION = 150; + + private final Handler mHoverToolTipHandler = new Handler(Looper.getMainLooper()); + private final Runnable mRevealHoverToolTipRunnable = this::revealHoverToolTip; + private final Runnable mHideHoverToolTipRunnable = this::hideHoverToolTip; + + private final TaskbarActivityContext mActivity; + private final TaskbarView mTaskbarView; + private final View mHoverView; + private final ArrowTipView mHoverToolTipView; + private final String mToolTipText; + + public TaskbarHoverToolTipController(TaskbarActivityContext activity, TaskbarView taskbarView, + View hoverView) { + mActivity = activity; + mTaskbarView = taskbarView; + mHoverView = hoverView; + + if (mHoverView instanceof BubbleTextView) { + mToolTipText = ((BubbleTextView) mHoverView).getText().toString(); + } else if (mHoverView instanceof FolderIcon + && ((FolderIcon) mHoverView).mInfo.title != null) { + mToolTipText = ((FolderIcon) mHoverView).mInfo.title.toString(); + } else { + mToolTipText = null; + } + + ContextThemeWrapper arrowContextWrapper = new ContextThemeWrapper(mActivity, + R.style.ArrowTipTaskbarStyle); + mHoverToolTipView = new ArrowTipView(arrowContextWrapper, /* isPointingUp = */ false, + R.layout.arrow_toast); + + AnimatorSet hoverCloseAnimator = new AnimatorSet(); + ObjectAnimator textCloseAnimator = ObjectAnimator.ofInt(mHoverToolTipView, TEXT_ALPHA, 0); + textCloseAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0, 0.33f)); + ObjectAnimator alphaCloseAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, ALPHA, 0); + alphaCloseAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0.33f, 0.66f)); + ObjectAnimator scaleCloseAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, SCALE_Y, 0); + scaleCloseAnimator.setInterpolator(Interpolators.STANDARD); + hoverCloseAnimator.playTogether( + textCloseAnimator, + alphaCloseAnimator, + scaleCloseAnimator); + hoverCloseAnimator.setStartDelay(0); + hoverCloseAnimator.setDuration(HOVER_TOOL_TIP_EXIT_DURATION); + mHoverToolTipView.setCustomCloseAnimation(hoverCloseAnimator); + + AnimatorSet hoverOpenAnimator = new AnimatorSet(); + ObjectAnimator textOpenAnimator = ObjectAnimator.ofInt(mHoverToolTipView, TEXT_ALPHA, 255); + textOpenAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0.33f, 1f)); + ObjectAnimator scaleOpenAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, SCALE_Y, 1f); + scaleOpenAnimator.setInterpolator(Interpolators.EMPHASIZED); + ObjectAnimator alphaOpenAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, ALPHA, 1f); + alphaOpenAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0.1f, 0.33f)); + hoverOpenAnimator.playTogether( + scaleOpenAnimator, + textOpenAnimator, + alphaOpenAnimator); + hoverOpenAnimator.setStartDelay(HOVER_TOOL_TIP_REVEAL_START_DELAY); + hoverOpenAnimator.setDuration(HOVER_TOOL_TIP_REVEAL_DURATION); + mHoverToolTipView.setCustomOpenAnimation(hoverOpenAnimator); + + mHoverToolTipView.addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { + mHoverToolTipView.setPivotY(bottom); + mHoverToolTipView.setY(mTaskbarView.getTop() - (bottom - top)); + }); + mHoverToolTipView.setScaleY(0f); + mHoverToolTipView.setAlpha(0f); + } + + @Override + public boolean onHover(View v, MotionEvent event) { + boolean isAnyOtherFloatingViewOpen = + AbstractFloatingView.hasOpenView(mActivity, TYPE_ALL_EXCEPT_ON_BOARD_POPUP); + if (isAnyOtherFloatingViewOpen) { + mHoverToolTipHandler.removeCallbacksAndMessages(null); + } + // If hover leaves a taskbar icon animate the tooltip closed. + if (event.getAction() == ACTION_HOVER_EXIT) { + startHideHoverToolTip(); + mActivity.setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, false); + return true; + } else if (!isAnyOtherFloatingViewOpen && event.getAction() == ACTION_HOVER_ENTER) { + // If hovering above a taskbar icon starts, animate the tooltip open. Do not + // reveal if any floating views such as folders or edu pop-ups are open. + startRevealHoverToolTip(); + mActivity.setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, true); + return true; + } + return false; + } + + private void startRevealHoverToolTip() { + mHoverToolTipHandler.postDelayed(mRevealHoverToolTipRunnable, + HOVER_TOOL_TIP_REVEAL_START_DELAY); + } + + private void revealHoverToolTip() { + if (mHoverView == null || mToolTipText == null) { + return; + } + if (mHoverView instanceof FolderIcon && !((FolderIcon) mHoverView).getIconVisible()) { + return; + } + mActivity.setTaskbarWindowFullscreen(true); + Rect iconViewBounds = Utilities.getViewBounds(mHoverView); + mHoverToolTipView.showAtLocation(mToolTipText, iconViewBounds.centerX(), + mTaskbarView.getTop(), /* shouldAutoClose= */ false); + } + + private void startHideHoverToolTip() { + mHoverToolTipHandler.removeCallbacks(mRevealHoverToolTipRunnable); + int accessibilityHideTimeout = AccessibilityManagerCompat.getRecommendedTimeoutMillis( + mActivity, /* originalTimeout= */ 0, FLAG_CONTENT_TEXT); + mHoverToolTipHandler.postDelayed(mHideHoverToolTipRunnable, accessibilityHideTimeout); + } + + private void hideHoverToolTip() { + mHoverToolTipView.close(/* animate = */ true); + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt index d6e559a9b4..5a4534e651 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt @@ -15,12 +15,15 @@ */ package com.android.launcher3.taskbar +import android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR import android.graphics.Insets import android.graphics.Region import android.os.Binder import android.os.IBinder +import android.view.Gravity import android.view.InsetsFrameProvider import android.view.InsetsFrameProvider.SOURCE_DISPLAY +import android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER import android.view.InsetsSource.FLAG_SUPPRESS_SCRIM import android.view.ViewTreeObserver import android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME @@ -41,6 +44,7 @@ import com.android.launcher3.anim.AlphaUpdateListener import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController import com.android.launcher3.util.DisplayController import java.io.PrintWriter +import kotlin.jvm.optionals.getOrNull /** Handles the insets that Taskbar provides to underlying apps and the IME. */ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTaskbarController { @@ -55,13 +59,13 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas private val touchableRegion: Region = Region() private val insetsOwner: IBinder = Binder() private val deviceProfileChangeListener = { _: DeviceProfile -> - onTaskbarWindowHeightOrInsetsChanged() + onTaskbarOrBubblebarWindowHeightOrInsetsChanged() } private val gestureNavSettingsObserver = GestureNavigationSettingsObserver( context.mainThreadHandler, context, - this::onTaskbarWindowHeightOrInsetsChanged + this::onTaskbarOrBubblebarWindowHeightOrInsetsChanged ) // Initialized in init. @@ -71,7 +75,7 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas fun init(controllers: TaskbarControllers) { this.controllers = controllers windowLayoutParams = context.windowLayoutParams - onTaskbarWindowHeightOrInsetsChanged() + onTaskbarOrBubblebarWindowHeightOrInsetsChanged() context.addOnDeviceProfileChangeListener(deviceProfileChangeListener) gestureNavSettingsObserver.registerForCallingUser() @@ -82,109 +86,165 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas gestureNavSettingsObserver.unregister() } - fun onTaskbarWindowHeightOrInsetsChanged() { - if (context.isGestureNav) { - windowLayoutParams.providedInsets = - arrayOf( - InsetsFrameProvider(insetsOwner, 0, navigationBars()) - .setFlags(FLAG_SUPPRESS_SCRIM, FLAG_SUPPRESS_SCRIM), - InsetsFrameProvider(insetsOwner, 0, tappableElement()), - InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()), - InsetsFrameProvider(insetsOwner, INDEX_LEFT, systemGestures()) - .setSource(SOURCE_DISPLAY), - InsetsFrameProvider(insetsOwner, INDEX_RIGHT, systemGestures()) - .setSource(SOURCE_DISPLAY) - ) - } else { - windowLayoutParams.providedInsets = - arrayOf( - InsetsFrameProvider(insetsOwner, 0, navigationBars()), - InsetsFrameProvider(insetsOwner, 0, tappableElement()), - InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()) - ) - } - - val touchableHeight = controllers.taskbarStashController.touchableHeight - touchableRegion.set( - 0, - windowLayoutParams.height - touchableHeight, - context.deviceProfile.widthPx, - windowLayoutParams.height - ) - val contentHeight = controllers.taskbarStashController.contentHeightToReportToApps + fun onTaskbarOrBubblebarWindowHeightOrInsetsChanged() { val tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps - val res = context.resources - for (provider in windowLayoutParams.providedInsets) { - if (provider.type == navigationBars() || provider.type == mandatorySystemGestures()) { - provider.insetsSize = getInsetsByNavMode(contentHeight) - } else if (provider.type == tappableElement()) { - provider.insetsSize = getInsetsByNavMode(tappableHeight) - } else if (provider.type == systemGestures() && provider.index == INDEX_LEFT) { - provider.insetsSize = - Insets.of( - gestureNavSettingsObserver.getLeftSensitivityForCallingUser(res), - 0, - 0, - 0 - ) - } else if (provider.type == systemGestures() && provider.index == INDEX_RIGHT) { - provider.insetsSize = - Insets.of( - 0, - 0, - gestureNavSettingsObserver.getRightSensitivityForCallingUser(res), - 0 - ) + // We only report tappableElement height for unstashed, persistent taskbar, + // which is also when we draw the rounded corners above taskbar. + val insetsRoundedCornerFlag = + if (tappableHeight > 0) { + FLAG_INSETS_ROUNDED_CORNER + } else { + 0 + } + + windowLayoutParams.providedInsets = getProvidedInsets(insetsRoundedCornerFlag) + if (!context.isGestureNav) { + if (windowLayoutParams.paramsForRotation != null) { + for (layoutParams in windowLayoutParams.paramsForRotation) { + layoutParams.providedInsets = getProvidedInsets(insetsRoundedCornerFlag) + } } } - val imeInsetsSize = getInsetsByNavMode(taskbarHeightForIme) - val insetsSizeOverride = - arrayOf( - InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize), + val taskbarTouchableHeight = controllers.taskbarStashController.touchableHeight + val bubblesTouchableHeight = + if (controllers.bubbleControllers.isPresent) { + controllers.bubbleControllers.get().bubbleStashController.touchableHeight + } else { + 0 + } + val touchableHeight = Math.max(taskbarTouchableHeight, bubblesTouchableHeight) + + if ( + controllers.bubbleControllers.isPresent && + controllers.bubbleControllers.get().bubbleStashController.isBubblesShowingOnHome + ) { + val iconBounds = + controllers.bubbleControllers.get().bubbleBarViewController.bubbleBarBounds + touchableRegion.set( + iconBounds.left, + iconBounds.top, + iconBounds.right, + iconBounds.bottom ) - // Use 0 tappableElement insets for the VoiceInteractionWindow when gesture nav is enabled. - val visInsetsSizeForGestureNavTappableElement = getInsetsByNavMode(0) - val insetsSizeOverrideForGestureNavTappableElement = - arrayOf( - InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize), - InsetsFrameProvider.InsetsSizeOverride( - TYPE_VOICE_INTERACTION, - visInsetsSizeForGestureNavTappableElement - ), + } else { + touchableRegion.set( + 0, + windowLayoutParams.height - touchableHeight, + context.deviceProfile.widthPx, + windowLayoutParams.height ) + } + + val gravity = windowLayoutParams.gravity for (provider in windowLayoutParams.providedInsets) { - if (context.isGestureNav && provider.type == tappableElement()) { - provider.insetsSizeOverrides = insetsSizeOverrideForGestureNavTappableElement - } else if (provider.type != systemGestures()) { - // We only override insets at the bottom of the screen - provider.insetsSizeOverrides = insetsSizeOverride - } + setProviderInsets(provider, gravity) } - // We only report tappableElement height for unstashed, persistent taskbar, - // which is also when we draw the rounded corners above taskbar. - windowLayoutParams.insetsRoundedCornerFrame = tappableHeight > 0 + if (windowLayoutParams.paramsForRotation != null) { + // Add insets for navbar rotated params + for (layoutParams in windowLayoutParams.paramsForRotation) { + for (provider in layoutParams.providedInsets) { + setProviderInsets(provider, layoutParams.gravity) + } + } + } context.notifyUpdateLayoutParams() } /** - * @return [Insets] where the [bottomInset] is either used as a bottom inset or - * - * ``` - * right/left inset if using 3 button nav - * ``` + * The inset types and number of insets provided have to match for both gesture nav and button + * nav. The values and the order of the elements in array are allowed to differ. + * Reason being WM does not allow types and number of insets changing for a given window once it + * is added into the hierarchy for performance reasons. + */ + private fun getProvidedInsets(insetsRoundedCornerFlag: Int): Array<InsetsFrameProvider> { + val navBarsFlag = + (if (context.isGestureNav) FLAG_SUPPRESS_SCRIM else 0) or insetsRoundedCornerFlag + return arrayOf( + InsetsFrameProvider(insetsOwner, 0, navigationBars()) + .setFlags( + navBarsFlag, + FLAG_SUPPRESS_SCRIM or FLAG_INSETS_ROUNDED_CORNER + ), + InsetsFrameProvider(insetsOwner, 0, tappableElement()), + InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()), + InsetsFrameProvider(insetsOwner, INDEX_LEFT, systemGestures()) + .setSource(SOURCE_DISPLAY), + InsetsFrameProvider(insetsOwner, INDEX_RIGHT, systemGestures()) + .setSource(SOURCE_DISPLAY) + ) + } + + private fun setProviderInsets(provider: InsetsFrameProvider, gravity: Int) { + val contentHeight = controllers.taskbarStashController.contentHeightToReportToApps + val tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps + val res = context.resources + if (provider.type == navigationBars() || provider.type == mandatorySystemGestures()) { + provider.insetsSize = getInsetsForGravity(contentHeight, gravity) + } else if (provider.type == tappableElement()) { + provider.insetsSize = getInsetsForGravity(tappableHeight, gravity) + } else if (provider.type == systemGestures() && provider.index == INDEX_LEFT) { + val leftIndexInset = + if (context.isThreeButtonNav) 0 + else gestureNavSettingsObserver.getLeftSensitivityForCallingUser(res) + provider.insetsSize = Insets.of(leftIndexInset, 0, 0, 0) + } else if (provider.type == systemGestures() && provider.index == INDEX_RIGHT) { + val rightIndexInset = + if (context.isThreeButtonNav) 0 + else gestureNavSettingsObserver.getRightSensitivityForCallingUser(res) + provider.insetsSize = Insets.of(0, 0, rightIndexInset, 0) + } + + + // When in gesture nav, report the stashed height to the IME, to allow hiding the + // IME navigation bar. + val imeInsetsSize = if (ENABLE_HIDE_IME_CAPTION_BAR && context.isGestureNav) { + getInsetsForGravity(controllers.taskbarStashController.stashedHeight, gravity); + } else { + getInsetsForGravity(taskbarHeightForIme, gravity) + } + val imeInsetsSizeOverride = + arrayOf( + InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize), + ) + // Use 0 tappableElement insets for the VoiceInteractionWindow when gesture nav is enabled. + val visInsetsSizeForTappableElement = + if (context.isGestureNav) getInsetsForGravity(0, gravity) + else getInsetsForGravity(tappableHeight, gravity) + val insetsSizeOverrideForTappableElement = + arrayOf( + InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize), + InsetsFrameProvider.InsetsSizeOverride( + TYPE_VOICE_INTERACTION, + visInsetsSizeForTappableElement + ), + ) + if ((context.isGestureNav || TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + && provider.type == tappableElement()) { + provider.insetsSizeOverrides = insetsSizeOverrideForTappableElement + } else if (provider.type != systemGestures()) { + // We only override insets at the bottom of the screen + provider.insetsSizeOverrides = imeInsetsSizeOverride + } + } + + /** + * @return [Insets] where the [inset] is either used as a bottom inset or + * right/left inset if using 3 button nav */ - private fun getInsetsByNavMode(bottomInset: Int): Insets { - val devicePortrait = !context.deviceProfile.isLandscape - if (!TaskbarManager.isPhoneButtonNavMode(context) || devicePortrait) { + private fun getInsetsForGravity(inset: Int, gravity: Int): Insets { + if ((gravity and Gravity.BOTTOM) == Gravity.BOTTOM) { // Taskbar or portrait phone mode - return Insets.of(0, 0, 0, bottomInset) + return Insets.of(0, 0, 0, inset) } // TODO(b/230394142): seascape - return Insets.of(0, 0, bottomInset, 0) + val isSeascape = (gravity and Gravity.START) == Gravity.START + val leftInset = if (isSeascape) inset else 0 + val rightInset = if (isSeascape) 0 else inset + return Insets.of(leftInset , 0, rightInset, 0) } /** @@ -199,6 +259,9 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas context.dragLayer, insetsInfo.touchableRegion ) + val bubbleBarVisible = + controllers.bubbleControllers.isPresent && + controllers.bubbleControllers.get().bubbleBarViewController.isBubbleBarVisible() var insetsIsTouchableRegion = true if (context.dragLayer.alpha < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) { // Let touches pass through us. @@ -219,16 +282,33 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME) insetsIsTouchableRegion = false } else if ( - controllers.taskbarViewController.areIconsVisible() || context.isNavBarKidsModeActive + controllers.taskbarViewController.areIconsVisible() || + context.isNavBarKidsModeActive || + bubbleBarVisible ) { // Taskbar has some touchable elements, take over the full taskbar area if ( controllers.uiController.isInOverview && DisplayController.isTransientTaskbar(context) ) { - insetsInfo.touchableRegion.set( + val region = controllers.taskbarActivityContext.dragLayer.lastDrawnTransientRect.toRegion() - ) + val bubbleBarBounds = + controllers.bubbleControllers.getOrNull()?.let { bubbleControllers -> + if (!bubbleControllers.bubbleStashController.isBubblesShowingOnOverview) { + return@let null + } + if (!bubbleControllers.bubbleBarViewController.isBubbleBarVisible) { + return@let null + } + bubbleControllers.bubbleBarViewController.bubbleBarBounds + } + + // Include the bounds of the bubble bar in the touchable region if they exist. + if (bubbleBarBounds != null) { + region.op(bubbleBarBounds, Region.Op.UNION) + } + insetsInfo.touchableRegion.set(region) } else { insetsInfo.touchableRegion.set(touchableRegion) } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index ddea51f737..90f7beaf9c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -204,7 +204,7 @@ public class TaskbarLauncherStateController { updateStateForFlag(FLAG_LAUNCHER_IN_STATE_TRANSITION, false); // TODO(b/279514548) Cleans up bad state that can occur when user interacts with // taskbar on top of transparent activity. - if (finalState == LauncherState.NORMAL && mLauncher.isResumed()) { + if (finalState == LauncherState.NORMAL && mLauncher.hasBeenResumed()) { updateStateForFlag(FLAG_RESUMED, true); } applyState(); @@ -249,7 +249,6 @@ public class TaskbarLauncherStateController { mIconAlignment.finishAnimation(); - Log.d("b/260135164", "onDestroy - updateIconAlphaForHome(1)"); mLauncher.getHotseat().setIconsAlpha(1f); mLauncher.getStateManager().removeStateListener(mStateListener); @@ -409,6 +408,14 @@ public class TaskbarLauncherStateController { + ", mLauncherState: " + mLauncherState + ", toAlignment: " + toAlignment); } + mControllers.bubbleControllers.ifPresent(controllers -> { + // Show the bubble bar when on launcher home or in overview. + boolean onHome = isInLauncher && mLauncherState == LauncherState.NORMAL; + boolean onOverview = mLauncherState == LauncherState.OVERVIEW; + controllers.bubbleStashController.setBubblesShowingOnHome(onHome); + controllers.bubbleStashController.setBubblesShowingOnOverview(onOverview); + }); + AnimatorSet animatorSet = new AnimatorSet(); if (hasAnyFlag(changedFlags, FLAG_LAUNCHER_IN_STATE_TRANSITION)) { @@ -479,7 +486,8 @@ public class TaskbarLauncherStateController { public void onAnimationEnd(Animator animation) { TaskbarStashController stashController = mControllers.taskbarStashController; - stashController.updateAndAnimateTransientTaskbar(/* stash */ true); + stashController.updateAndAnimateTransientTaskbar( + /* stash */ true, /* bubblesShouldFollow */ true); } }); } else { @@ -636,8 +644,6 @@ public class TaskbarLauncherStateController { public void onAnimationEnd(Animator animation) { if (isInStashedState && committed) { // Reset hotseat alpha to default - Log.d("b/260135164", - "playStateTransitionAnim#onAnimationEnd - setIconsAlpha(1)"); mLauncher.getHotseat().setIconsAlpha(1); } } @@ -705,8 +711,6 @@ public class TaskbarLauncherStateController { private void updateIconAlphaForHome(float alpha) { if (mControllers.taskbarActivityContext.isDestroyed()) { - Log.e("b/260135164", "updateIconAlphaForHome is called after Taskbar is destroyed", - new Exception()); return; } mIconAlphaForHome.setValue(alpha); @@ -717,9 +721,6 @@ public class TaskbarLauncherStateController { * Hide Launcher Hotseat icons when Taskbar icons have opacity. Both icon sets * should not be visible at the same time. */ - Log.d("b/260135164", - "updateIconAlphaForHome - setIconsAlpha(" + (hotseatVisible ? 1 : 0) - + "), isTaskbarPresent: " + mLauncher.getDeviceProfile().isTaskbarPresent); mLauncher.getHotseat().setIconsAlpha(hotseatVisible ? 1 : 0); if (mIsQsbInline) { mLauncher.getHotseat().setQsbAlpha(hotseatVisible ? 1 : 0); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java index 738ff87744..b115ca8032 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -22,13 +22,13 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING; import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_KEY; -import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY; -import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE; +import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.util.DisplayController.TASKBAR_NOT_DESTROYED_TAG; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange; import android.annotation.SuppressLint; +import android.app.Activity; import android.app.PendingIntent; import android.content.ComponentCallbacks; import android.content.Context; @@ -41,6 +41,7 @@ import android.hardware.display.DisplayManager; import android.net.Uri; import android.os.Handler; import android.os.SystemProperties; +import android.os.Trace; import android.provider.Settings; import android.util.Log; import android.view.Display; @@ -50,19 +51,20 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider; import com.android.launcher3.uioverrides.QuickstepLauncher; -import com.android.launcher3.util.DisplayController; -import com.android.launcher3.util.NavigationMode; +import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter; import com.android.launcher3.util.SettingsCache; import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.quickstep.RecentsActivity; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TouchInteractionService; +import com.android.quickstep.util.AssistUtils; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.unfold.UnfoldTransitionProgressProvider; import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider; @@ -77,6 +79,22 @@ public class TaskbarManager { private static final String TAG = "TaskbarManager"; private static final boolean DEBUG = false; + /** + * All the configurations which do not initiate taskbar recreation. + * This includes all the configurations defined in Launcher's manifest entry and + * ActivityController#filterConfigChanges + */ + private static final int SKIP_RECREATE_CONFIG_CHANGES = ActivityInfo.CONFIG_WINDOW_CONFIGURATION + | ActivityInfo.CONFIG_KEYBOARD + | ActivityInfo.CONFIG_KEYBOARD_HIDDEN + | ActivityInfo.CONFIG_MCC + | ActivityInfo.CONFIG_MNC + | ActivityInfo.CONFIG_NAVIGATION + | ActivityInfo.CONFIG_ORIENTATION + | ActivityInfo.CONFIG_SCREEN_SIZE + | ActivityInfo.CONFIG_SCREEN_LAYOUT + | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; + public static final boolean FLAG_HIDE_NAVBAR_WINDOW = SystemProperties.getBoolean("persist.wm.debug.hide_navbar_window", false); @@ -87,12 +105,11 @@ public class TaskbarManager { Settings.Secure.NAV_BAR_KIDS_MODE); private final Context mContext; - private final DisplayController mDisplayController; private final TaskbarNavButtonController mNavButtonController; - private final SettingsCache.OnChangeListener mUserSetupCompleteListener; - private final SettingsCache.OnChangeListener mNavBarKidsModeListener; private final ComponentCallbacks mComponentCallbacks; - private final SimpleBroadcastReceiver mShutdownReceiver; + + private final SimpleBroadcastReceiver mShutdownReceiver = + new SimpleBroadcastReceiver(i -> destroyExistingTaskbar()); // The source for this provider is set when Launcher is available // We use 'non-destroyable' version here so the original provider won't be destroyed @@ -100,7 +117,6 @@ public class TaskbarManager { // It's destruction/creation will be managed by the activity. private final ScopedUnfoldTransitionProgressProvider mUnfoldProgressProvider = new NonDestroyableScopedUnfoldTransitionProgressProvider(); - private NavigationMode mNavMode; private TaskbarActivityContext mTaskbarActivityContext; private StatefulActivity mActivity; @@ -111,19 +127,11 @@ public class TaskbarManager { private final TaskbarSharedState mSharedState = new TaskbarSharedState(); /** - * We use WindowManager's ComponentCallbacks() for most of the config changes, however for - * navigation mode, that callback gets called too soon, before it's internal navigation mode - * reflects the current one. - * DisplayController's callback is delayed enough to get the correct nav mode value - * - * We also use density change here because DeviceProfile has had a chance to update it's state - * whereas density for component callbacks registered in this class don't update DeviceProfile. - * Confused? Me too. Make it less confusing (TODO: b/227669780) - * - * Flags used with {@link #mDispInfoChangeListener} + * We use WindowManager's ComponentCallbacks() for internal UI changes (similar to an Activity) + * which comes via a different channel */ - private static final int CHANGE_FLAGS = CHANGE_NAVIGATION_MODE | CHANGE_DENSITY; - private final DisplayController.DisplayInfoChangeListener mDispInfoChangeListener; + private final OnIDPChangeListener mIdpChangeListener = c -> recreateTaskbar(); + private final SettingsCache.OnChangeListener mOnSettingsChangeListener = c -> recreateTaskbar(); private boolean mUserUnlocked = false; @@ -144,101 +152,121 @@ public class TaskbarManager { } }; + private final ActivityLifecycleCallbacksAdapter mLifecycleCallbacks = + new ActivityLifecycleCallbacksAdapter() { + @Override + public void onActivityDestroyed(Activity activity) { + if (mActivity != activity) return; + if (mActivity != null) { + mActivity.removeOnDeviceProfileChangeListener( + mDebugActivityDeviceProfileChanged); + Log.d(TASKBAR_NOT_DESTROYED_TAG, + "unregistering activity lifecycle callbacks from " + + "onActivityDestroyed."); + mActivity.unregisterActivityLifecycleCallbacks(this); + } + mActivity = null; + debugWhyTaskbarNotDestroyed("clearActivity"); + if (mTaskbarActivityContext != null) { + mTaskbarActivityContext.setUIController(TaskbarUIController.DEFAULT); + } + mUnfoldProgressProvider.setSourceProvider(null); + } + }; + + UnfoldTransitionProgressProvider.TransitionProgressListener mUnfoldTransitionProgressListener = + new UnfoldTransitionProgressProvider.TransitionProgressListener() { + @Override + public void onTransitionStarted() { + Log.d(TASKBAR_NOT_DESTROYED_TAG, + "fold/unfold transition started getting called."); + } + + @Override + public void onTransitionProgress(float progress) { + Log.d(TASKBAR_NOT_DESTROYED_TAG, + "fold/unfold transition progress : " + progress); + } + + @Override + public void onTransitionFinishing() { + Log.d(TASKBAR_NOT_DESTROYED_TAG, + "fold/unfold transition finishing getting called."); + + } + + @Override + public void onTransitionFinished() { + Log.d(TASKBAR_NOT_DESTROYED_TAG, + "fold/unfold transition finished getting called."); + + } + }; + @SuppressLint("WrongConstant") public TaskbarManager(TouchInteractionService service) { - mDisplayController = DisplayController.INSTANCE.get(service); Display display = service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY); mContext = service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null); mNavButtonController = new TaskbarNavButtonController(service, - SystemUiProxy.INSTANCE.get(mContext), new Handler()); - mUserSetupCompleteListener = isUserSetupComplete -> recreateTaskbar(); - mNavBarKidsModeListener = isNavBarKidsMode -> recreateTaskbar(); - // TODO(b/227669780): Consolidate this w/ DisplayController callbacks + SystemUiProxy.INSTANCE.get(mContext), new Handler(), + AssistUtils.newInstance(mContext)); mComponentCallbacks = new ComponentCallbacks() { private Configuration mOldConfig = mContext.getResources().getConfiguration(); @Override public void onConfigurationChanged(Configuration newConfig) { + Trace.instantForTrack(Trace.TRACE_TAG_APP, "TaskbarManager", + "onConfigurationChanged: " + newConfig); debugWhyTaskbarNotDestroyed( "TaskbarManager#mComponentCallbacks.onConfigurationChanged: " + newConfig); DeviceProfile dp = mUserUnlocked ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null; - int configDiff = mOldConfig.diff(newConfig); - int configDiffForRecreate = configDiff; - int configsRequiringRecreate = ActivityInfo.CONFIG_ASSETS_PATHS - | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_UI_MODE - | ActivityInfo.CONFIG_SCREEN_SIZE; - if ((configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0 - && mTaskbarActivityContext != null && dp != null - && !isPhoneMode(dp)) { - // Additional check since this callback gets fired multiple times w/o - // screen size changing, or when simply rotating the device. - // In the case of phone device rotation, we do want to call recreateTaskbar() - DeviceProfile oldDp = mTaskbarActivityContext.getDeviceProfile(); - boolean isOrientationChange = - (configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0; - - int newOrientation = newConfig.windowConfiguration.getRotation(); - int oldOrientation = mOldConfig.windowConfiguration.getRotation(); - int oldWidth = isOrientationChange ? oldDp.heightPx : oldDp.widthPx; - int oldHeight = isOrientationChange ? oldDp.widthPx : oldDp.heightPx; - - if ((dp.widthPx == oldWidth && dp.heightPx == oldHeight) - || (newOrientation == oldOrientation)) { - configDiffForRecreate &= ~ActivityInfo.CONFIG_SCREEN_SIZE; - } - } + int configDiff = mOldConfig.diff(newConfig) & ~SKIP_RECREATE_CONFIG_CHANGES; + if ((configDiff & ActivityInfo.CONFIG_UI_MODE) != 0) { // Only recreate for theme changes, not other UI mode changes such as docking. int oldUiNightMode = (mOldConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK); int newUiNightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK); if (oldUiNightMode == newUiNightMode) { - configDiffForRecreate &= ~ActivityInfo.CONFIG_UI_MODE; + configDiff &= ~ActivityInfo.CONFIG_UI_MODE; } } debugWhyTaskbarNotDestroyed("ComponentCallbacks#onConfigurationChanged() " - + "configDiffForRecreate=" - + Configuration.configurationDiffToString(configDiffForRecreate)); - if ((configDiffForRecreate & configsRequiringRecreate) != 0) { + + "configDiff=" + Configuration.configurationDiffToString(configDiff)); + if (configDiff != 0 || mTaskbarActivityContext == null) { recreateTaskbar(); } else { // Config change might be handled without re-creating the taskbar - if (mTaskbarActivityContext != null) { - if (dp != null && !isTaskbarPresent(dp)) { - destroyExistingTaskbar(); - } else { - if (dp != null && isTaskbarPresent(dp)) { - mTaskbarActivityContext.updateDeviceProfile(dp, mNavMode); + if (dp != null && !isTaskbarPresent(dp)) { + destroyExistingTaskbar(); + } else { + if (dp != null && isTaskbarPresent(dp)) { + if (FLAG_HIDE_NAVBAR_WINDOW) { + // Re-initialize for screen size change? Should this be done + // by looking at screen-size change flag in configDiff in the + // block above? + recreateTaskbar(); + } else { + mTaskbarActivityContext.updateDeviceProfile(dp); } - mTaskbarActivityContext.onConfigurationChanged(configDiff); } + mTaskbarActivityContext.onConfigurationChanged(configDiff); } } - mOldConfig = newConfig; + mOldConfig = new Configuration(newConfig); } @Override public void onLowMemory() { } }; - mShutdownReceiver = new SimpleBroadcastReceiver(i -> - destroyExistingTaskbar()); - mDispInfoChangeListener = (context, info, flags) -> { - if ((flags & CHANGE_FLAGS) != 0) { - mNavMode = info.navigationMode; - recreateTaskbar(); - } - debugWhyTaskbarNotDestroyed("DisplayInfoChangeListener#" - + mDisplayController.getChangeFlagsString(flags)); - }; - mNavMode = mDisplayController.getInfo().navigationMode; - mDisplayController.addChangeListener(mDispInfoChangeListener); - SettingsCache.INSTANCE.get(mContext).register(USER_SETUP_COMPLETE_URI, - mUserSetupCompleteListener); - SettingsCache.INSTANCE.get(mContext).register(NAV_BAR_KIDS_MODE, - mNavBarKidsModeListener); + SettingsCache.INSTANCE.get(mContext) + .register(USER_SETUP_COMPLETE_URI, mOnSettingsChangeListener); + SettingsCache.INSTANCE.get(mContext) + .register(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener); + Log.d(TASKBAR_NOT_DESTROYED_TAG, "registering component callbacks from constructor."); mContext.registerComponentCallbacks(mComponentCallbacks); mShutdownReceiver.register(mContext, Intent.ACTION_SHUTDOWN); UI_HELPER_EXECUTOR.execute(() -> { @@ -279,6 +307,25 @@ public class TaskbarManager { } /** + * Toggles All Apps for Taskbar or Launcher depending on the current state. + * + * @param homeAllAppsIntent Intent used if Taskbar is not enabled or Launcher is resumed. + */ + public void toggleAllApps(Intent homeAllAppsIntent) { + if (mTaskbarActivityContext == null) { + mContext.startActivity(homeAllAppsIntent); + return; + } + + if (mActivity != null && mActivity.isResumed() && !mActivity.isInState(OVERVIEW)) { + mContext.startActivity(homeAllAppsIntent); + return; + } + + mTaskbarActivityContext.toggleAllApps(); + } + + /** * Displays a frame of the first Launcher reveal animation. * * This should be used to run a first Launcher reveal animation whose progress matches a swipe @@ -294,6 +341,7 @@ public class TaskbarManager { */ public void onUserUnlocked() { mUserUnlocked = true; + LauncherAppState.getIDP(mContext).addOnChangeListener(mIdpChangeListener); recreateTaskbar(); } @@ -304,14 +352,18 @@ public class TaskbarManager { if (mActivity == activity) { return; } - if (mActivity != null) { - mActivity.removeOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged); - } + removeActivityCallbacksAndListeners(); mActivity = activity; debugWhyTaskbarNotDestroyed("Set mActivity=" + mActivity); mActivity.addOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged); + Log.d(TASKBAR_NOT_DESTROYED_TAG, + "registering activity lifecycle callbacks from setActivity()."); + mActivity.registerActivityLifecycleCallbacks(mLifecycleCallbacks); UnfoldTransitionProgressProvider unfoldTransitionProgressProvider = getUnfoldTransitionProgressProviderForActivity(activity); + if (unfoldTransitionProgressProvider != null) { + unfoldTransitionProgressProvider.addCallback(mUnfoldTransitionProgressListener); + } mUnfoldProgressProvider.setSourceProvider(unfoldTransitionProgressProvider); if (mTaskbarActivityContext != null) { @@ -349,59 +401,50 @@ public class TaskbarManager { } /** - * Clears a previously set {@link StatefulActivity} - */ - public void clearActivity(@NonNull StatefulActivity activity) { - if (mActivity == activity) { - mActivity.removeOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged); - mActivity = null; - debugWhyTaskbarNotDestroyed("clearActivity"); - if (mTaskbarActivityContext != null) { - mTaskbarActivityContext.setUIController(TaskbarUIController.DEFAULT); - } - mUnfoldProgressProvider.setSourceProvider(null); - } - } - - /** * This method is called multiple times (ex. initial init, then when user unlocks) in which case * we fully want to destroy an existing taskbar and create a new one. * In other case (folding/unfolding) we don't need to remove and add window. */ @VisibleForTesting public void recreateTaskbar() { - DeviceProfile dp = mUserUnlocked ? + Trace.beginSection("recreateTaskbar"); + try { + DeviceProfile dp = mUserUnlocked ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null; - destroyExistingTaskbar(); + destroyExistingTaskbar(); - boolean isTaskbarEnabled = dp != null && isTaskbarPresent(dp); - debugWhyTaskbarNotDestroyed("recreateTaskbar: isTaskbarEnabled=" + isTaskbarEnabled + boolean isTaskbarEnabled = dp != null && isTaskbarPresent(dp); + debugWhyTaskbarNotDestroyed("recreateTaskbar: isTaskbarEnabled=" + isTaskbarEnabled + " [dp != null (i.e. mUserUnlocked)]=" + (dp != null) + " FLAG_HIDE_NAVBAR_WINDOW=" + FLAG_HIDE_NAVBAR_WINDOW + " dp.isTaskbarPresent=" + (dp == null ? "null" : dp.isTaskbarPresent)); - if (!isTaskbarEnabled) { - SystemUiProxy.INSTANCE.get(mContext) + if (!isTaskbarEnabled) { + SystemUiProxy.INSTANCE.get(mContext) .notifyTaskbarStatus(/* visible */ false, /* stashed */ false); - return; - } + return; + } - if (mTaskbarActivityContext == null) { - mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp, mNavButtonController, + if (mTaskbarActivityContext == null) { + mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp, + mNavButtonController, mUnfoldProgressProvider); - } else { - mTaskbarActivityContext.updateDeviceProfile(dp, mNavMode); - } - mTaskbarActivityContext.init(mSharedState); + } else { + mTaskbarActivityContext.updateDeviceProfile(dp); + } + mTaskbarActivityContext.init(mSharedState); - if (mActivity != null) { - mTaskbarActivityContext.setUIController( + if (mActivity != null) { + mTaskbarActivityContext.setUIController( createTaskbarUIControllerForActivity(mActivity)); - } + } - // We to wait until user unlocks the device to attach listener. - LauncherPrefs.get(mContext).addListener(mTaskbarPinningPreferenceChangeListener, + // We to wait until user unlocks the device to attach listener. + LauncherPrefs.get(mContext).addListener(mTaskbarPinningPreferenceChangeListener, TASKBAR_PINNING); + } finally { + Trace.endSection(); + } } public void onSystemUiFlagsChanged(int systemUiStateFlags) { @@ -481,23 +524,38 @@ public class TaskbarManager { } } + private void removeActivityCallbacksAndListeners() { + if (mActivity != null) { + mActivity.removeOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged); + Log.d(TASKBAR_NOT_DESTROYED_TAG, + "unregistering activity lifecycle callbacks from " + + "removeActivityCallbackAndListeners()."); + mActivity.unregisterActivityLifecycleCallbacks(mLifecycleCallbacks); + UnfoldTransitionProgressProvider unfoldTransitionProgressProvider = + getUnfoldTransitionProgressProviderForActivity(mActivity); + if (unfoldTransitionProgressProvider != null) { + unfoldTransitionProgressProvider.removeCallback(mUnfoldTransitionProgressListener); + } + } + } + /** * Called when the manager is no longer needed */ public void destroy() { debugWhyTaskbarNotDestroyed("TaskbarManager#destroy()"); - if (mActivity != null) { - mActivity.removeOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged); - } - + removeActivityCallbacksAndListeners(); UI_HELPER_EXECUTOR.execute( () -> mTaskbarBroadcastReceiver.unregisterReceiverSafely(mContext)); destroyExistingTaskbar(); - mDisplayController.removeChangeListener(mDispInfoChangeListener); - SettingsCache.INSTANCE.get(mContext).unregister(USER_SETUP_COMPLETE_URI, - mUserSetupCompleteListener); - SettingsCache.INSTANCE.get(mContext).unregister(NAV_BAR_KIDS_MODE, - mNavBarKidsModeListener); + if (mUserUnlocked) { + LauncherAppState.getIDP(mContext).removeOnChangeListener(mIdpChangeListener); + } + SettingsCache.INSTANCE.get(mContext) + .unregister(USER_SETUP_COMPLETE_URI, mOnSettingsChangeListener); + SettingsCache.INSTANCE.get(mContext) + .unregister(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener); + Log.d(TASKBAR_NOT_DESTROYED_TAG, "unregistering component callbacks from destroy()."); mContext.unregisterComponentCallbacks(mComponentCallbacks); mContext.unregisterReceiver(mShutdownReceiver); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java index 6cf63a9193..7692760834 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java @@ -57,7 +57,7 @@ public class TaskbarModelCallbacks implements private final TaskbarView mContainer; // Initialized in init. - private TaskbarControllers mControllers; + protected TaskbarControllers mControllers; // Used to defer any UI updates during the SUW unstash animation. private boolean mDeferUpdatesForSUW; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacksFactory.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacksFactory.kt new file mode 100644 index 0000000000..eb03b4abc5 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacksFactory.kt @@ -0,0 +1,42 @@ +/* + * 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.launcher3.taskbar + +import android.content.Context +import com.android.launcher3.R +import com.android.launcher3.util.ResourceBasedOverride +import com.android.launcher3.util.ResourceBasedOverride.Overrides + +/** Creates [TaskbarModelCallbacks] instances. */ +open class TaskbarModelCallbacksFactory : ResourceBasedOverride { + + open fun create( + activityContext: TaskbarActivityContext, + container: TaskbarView, + ): TaskbarModelCallbacks = TaskbarModelCallbacks(activityContext, container) + + companion object { + @JvmStatic + fun newInstance(context: Context): TaskbarModelCallbacksFactory { + return Overrides.getObject( + TaskbarModelCallbacksFactory::class.java, + context, + R.string.taskbar_model_callbacks_factory_class, + ) + } + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java index 610efebfcb..03df0365bb 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java @@ -43,12 +43,16 @@ import androidx.annotation.StringRes; import com.android.launcher3.R; import com.android.launcher3.logging.StatsLogManager; +import com.android.launcher3.statehandlers.DesktopVisibilityController; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; +import com.android.quickstep.LauncherActivityInterface; import com.android.quickstep.OverviewCommandHelper; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskUtils; import com.android.quickstep.TouchInteractionService; +import com.android.quickstep.util.AssistUtils; +import com.android.quickstep.views.DesktopTaskView; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -105,15 +109,17 @@ public class TaskbarNavButtonController implements TaskbarControllers.LoggableTa private final TouchInteractionService mService; private final SystemUiProxy mSystemUiProxy; private final Handler mHandler; + private final AssistUtils mAssistUtils; @Nullable private StatsLogManager mStatsLogManager; private final Runnable mResetLongPress = this::resetScreenUnpin; public TaskbarNavButtonController(TouchInteractionService service, - SystemUiProxy systemUiProxy, Handler handler) { + SystemUiProxy systemUiProxy, Handler handler, AssistUtils assistUtils) { mService = service; mSystemUiProxy = systemUiProxy; mHandler = handler; + mAssistUtils = assistUtils; } public void onButtonClick(@TaskbarButton int buttonType, View view) { @@ -155,7 +161,7 @@ public class TaskbarNavButtonController implements TaskbarControllers.LoggableTa switch (buttonType) { case BUTTON_HOME: logEvent(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS); - startAssistant(); + onLongPressHome(); return true; case BUTTON_A11Y: logEvent(LAUNCHER_TASKBAR_A11Y_BUTTON_LONGPRESS); @@ -267,6 +273,15 @@ public class TaskbarNavButtonController implements TaskbarControllers.LoggableTa private void navigateHome() { TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY); + + if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) { + DesktopVisibilityController desktopVisibilityController = + LauncherActivityInterface.INSTANCE.getDesktopVisibilityController(); + if (desktopVisibilityController != null) { + desktopVisibilityController.onHomeActionTriggered(); + } + } + mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_HOME); } @@ -295,13 +310,16 @@ public class TaskbarNavButtonController implements TaskbarControllers.LoggableTa } } - private void startAssistant() { + private void onLongPressHome() { if (mScreenPinned || !mAssistantLongPressEnabled) { return; } - Bundle args = new Bundle(); - args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS); - mSystemUiProxy.startAssistant(args); + // Attempt to start Assist with AssistUtils, otherwise fall back to SysUi's implementation. + if (!mAssistUtils.tryStartAssistOverride(INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS)) { + Bundle args = new Bundle(); + args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS); + mSystemUiProxy.startAssistant(args); + } } private void showQuickSettings() { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java index 5ea00cf408..1c250bf377 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.taskbar; +import static com.android.launcher3.taskbar.bubbles.BubbleBarController.BUBBLE_BAR_ENABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED; @@ -23,6 +24,7 @@ import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import com.android.launcher3.anim.AnimatedFloat; +import com.android.launcher3.util.DisplayController; import com.android.quickstep.SystemUiProxy; import java.io.PrintWriter; @@ -63,6 +65,10 @@ public class TaskbarScrimViewController implements TaskbarControllers.LoggableTa * Updates the scrim state based on the flags. */ public void updateStateForSysuiFlags(int stateFlags, boolean skipAnim) { + if (BUBBLE_BAR_ENABLED && DisplayController.isTransientTaskbar(mActivity)) { + // These scrims aren't used if bubble bar & transient taskbar are active. + return; + } final boolean bubblesExpanded = (stateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0; final boolean manageMenuExpanded = (stateFlags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index daf9c47eb0..a920dfaf6a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -18,11 +18,11 @@ package com.android.launcher3.taskbar; import static android.view.HapticFeedbackConstants.LONG_PRESS; import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS; +import static com.android.app.animation.Interpolators.EMPHASIZED; +import static com.android.app.animation.Interpolators.FINAL_FRAME; +import static com.android.app.animation.Interpolators.INSTANT; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_KEY; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED; -import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; -import static com.android.launcher3.anim.Interpolators.INSTANT; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_PINNING; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW; @@ -257,14 +257,15 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba private boolean mEnableBlockingTimeoutDuringTests = false; // Evaluate whether the handle should be stashed + private final IntPredicate mIsStashedPredicate = flags -> { + boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP); + boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP); + boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE); + boolean forceStashed = hasAnyFlag(flags, FLAGS_FORCE_STASHED); + return (inApp && stashedInApp) || (!inApp && stashedLauncherState) || forceStashed; + }; private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder( - flags -> { - boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP); - boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP); - boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE); - boolean forceStashed = hasAnyFlag(flags, FLAGS_FORCE_STASHED); - return (inApp && stashedInApp) || (!inApp && stashedLauncherState) || forceStashed; - }); + mIsStashedPredicate); private boolean mIsTaskbarSystemActionRegistered = false; private TaskbarSharedState mTaskbarSharedState; @@ -337,7 +338,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba // us that we're paused until a bit later. This avoids flickering upon recreating taskbar. updateStateForFlag(FLAG_IN_APP, true); applyState(/* duration = */ 0); - notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp()); } @@ -504,9 +504,19 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } /** - * Stash or unstashes the transient taskbar. + * Stash or unstashes the transient taskbar, using the default TASKBAR_STASH_DURATION. + * If bubble bar exists, it will match taskbars stashing behavior. */ public void updateAndAnimateTransientTaskbar(boolean stash) { + updateAndAnimateTransientTaskbar(stash, /* shouldBubblesFollow= */ true); + } + + /** + * Stash or unstashes the transient taskbar. + * @param stash whether transient taskbar should be stashed. + * @param shouldBubblesFollow whether bubbles should match taskbars behavior. + */ + public void updateAndAnimateTransientTaskbar(boolean stash, boolean shouldBubblesFollow) { if (!DisplayController.isTransientTaskbar(mActivity)) { return; } @@ -525,6 +535,34 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, stash); applyState(); } + + mControllers.bubbleControllers.ifPresent(controllers -> { + if (shouldBubblesFollow) { + final boolean willStash = mIsStashedPredicate.test(mState); + if (willStash != controllers.bubbleStashController.isStashed()) { + // Typically bubbles gets stashed / unstashed along with Taskbar, however, if + // taskbar is becoming stashed because bubbles is being expanded, we don't want + // to stash bubbles. + if (willStash) { + controllers.bubbleStashController.stashBubbleBar(); + } else { + controllers.bubbleStashController.showBubbleBar(false /* expandBubbles */); + } + } + } + }); + } + + /** + * Stashes transient taskbar after it has timed out. + */ + private void updateAndAnimateTransientTaskbarForTimeout() { + // If bubbles are expanded we shouldn't stash them when taskbar is hidden + // for the timeout. + boolean bubbleBarExpanded = mControllers.bubbleControllers.isPresent() + && mControllers.bubbleControllers.get().bubbleBarViewController.isExpanded(); + updateAndAnimateTransientTaskbar(/* stash= */ true, + /* shouldBubblesFollow= */ !bubbleBarExpanded); } /** @@ -636,7 +674,10 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba .setDuration(duration)); mAnimator.play(mTaskbarImeBgAlpha.animateToValue( hasAnyFlag(FLAG_STASHED_IN_APP_IME) ? 0 : 1).setDuration(duration)); - mAnimator.addListener(AnimatorListeners.forEndCallback(() -> mAnimator = null)); + mAnimator.addListener(AnimatorListeners.forEndCallback(() -> { + mAnimator = null; + mIsStashed = isStashed; + })); return; } @@ -830,8 +871,11 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba .setDuration(isStashed ? duration / 2 : duration)); } - private static void play(AnimatorSet as, Animator a, long startDelay, long duration, + private static void play(AnimatorSet as, @Nullable Animator a, long startDelay, long duration, Interpolator interpolator) { + if (a == null) { + return; + } a.setDuration(duration); a.setStartDelay(startDelay); a.setInterpolator(interpolator); @@ -897,7 +941,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba private void onIsStashedChanged(boolean isStashed) { mControllers.runAfterInit(() -> { mControllers.stashedHandleViewController.onIsStashedChanged(isStashed); - mControllers.taskbarInsetsController.onTaskbarWindowHeightOrInsetsChanged(); + mControllers.taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged(); }); } @@ -1053,6 +1097,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR, !hasAnyFlag(FLAG_STASHED_IN_APP_AUTO)); } + mActivity.applyForciblyShownFlagWhileTransientTaskbarUnstashed(!isStashedInApp()); } private void notifyStashChange(boolean visible, boolean stashed) { @@ -1147,7 +1192,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba if (mControllers.taskbarAutohideSuspendController.isTransientTaskbarStashingSuspended()) { return; } - updateAndAnimateTransientTaskbar(true); + updateAndAnimateTransientTaskbarForTimeout(); } @Override diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt index 1cc667211c..deaf0244e9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt @@ -16,9 +16,9 @@ package com.android.launcher3.taskbar import android.view.MotionEvent +import com.android.app.animation.Interpolators.LINEAR import com.android.launcher3.R import com.android.launcher3.Utilities -import com.android.launcher3.anim.Interpolators.LINEAR import com.android.launcher3.testing.shared.ResourceUtils import com.android.launcher3.touch.SingleAxisSwipeDetector import com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE @@ -108,7 +108,17 @@ class TaskbarStashViaTouchController(val controllers: TaskbarControllers) : Touc } override fun onControllerInterceptTouchEvent(ev: MotionEvent): Boolean { - if (!enabled || controllers.taskbarStashController.isStashed) { + if (!enabled) { + return false + } + val bubbleControllers = controllers.bubbleControllers.orElse(null) + if (bubbleControllers != null && bubbleControllers.bubbleBarViewController.isExpanded) { + return false + } + if ( + (bubbleControllers == null || bubbleControllers.bubbleStashController.isStashed) && + controllers.taskbarStashController.isStashed + ) { return false } @@ -122,7 +132,12 @@ class TaskbarStashViaTouchController(val controllers: TaskbarControllers) : Touc return true } } else if (ev.action == MotionEvent.ACTION_DOWN) { - if (screenCoordinatesEv.y < gestureHeightYThreshold) { + val isDownOnBubbleBar = + (bubbleControllers != null && + bubbleControllers.bubbleBarViewController.isEventOverAnyItem( + screenCoordinatesEv + )) + if (!isDownOnBubbleBar && screenCoordinatesEv.y < gestureHeightYThreshold) { controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true) } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarThresholdUtils.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarThresholdUtils.java new file mode 100644 index 0000000000..5b6fbef4fd --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarThresholdUtils.java @@ -0,0 +1,87 @@ +/* + * 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.launcher3.taskbar; + +import static com.android.launcher3.Utilities.dpToPx; +import static com.android.launcher3.Utilities.dpiFromPx; + +import android.content.res.Resources; +import android.util.DisplayMetrics; + +import androidx.core.content.res.ResourcesCompat; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.R; +import com.android.launcher3.config.FeatureFlags; + +/** + * Utility class that contains the different taskbar thresholds logic. + */ +public class TaskbarThresholdUtils { + + // We divide the screen into this many parts, and use the result to scale the thresholds to + // any size device. Note that this value was calculated arbitrarily by using two tablet devices + // as data points. + private static final float SCREEN_UNITS = 1 / 80f; + + private static int getThreshold(Resources r, DeviceProfile dp, int thresholdDimen, + int multiplierDimen) { + if (!FeatureFlags.ENABLE_DYNAMIC_TASKBAR_THRESHOLDS.get()) { + return r.getDimensionPixelSize(thresholdDimen); + } + + float landscapeScreenHeight = dp.isLandscape ? dp.heightPx : dp.widthPx; + float screenPart = (landscapeScreenHeight * SCREEN_UNITS); + float defaultDp = dpiFromPx(screenPart, DisplayMetrics.DENSITY_DEVICE_STABLE); + float thisDp = dpToPx(defaultDp); + float multiplier = ResourcesCompat.getFloat(r, multiplierDimen); + float value = (thisDp) * multiplier; + + return Math.round(value); + } + + /** + * Returns the threshold that determines if we should show taskbar. + */ + public static int getFromNavThreshold(Resources r, DeviceProfile dp) { + return getThreshold(r, dp, R.dimen.taskbar_from_nav_threshold, + R.dimen.taskbar_nav_threshold_mult); + } + + /** + * Returns the threshold that we start moving the app window. + */ + public static int getAppWindowThreshold(Resources r, DeviceProfile dp) { + return getThreshold(r, dp, R.dimen.taskbar_app_window_threshold, + R.dimen.taskbar_app_window_threshold_mult); + } + + /** + * Returns the threshold for whether we land in home or overview. + */ + public static int getHomeOverviewThreshold(Resources r, DeviceProfile dp) { + return getThreshold(r, dp, R.dimen.taskbar_home_overview_threshold, + R.dimen.taskbar_home_overview_threshold_mult); + } + + /** + * Returns the threshold that we use to allow swipe to catch up to finger. + */ + public static int getCatchUpThreshold(Resources r, DeviceProfile dp) { + return getThreshold(r, dp, R.dimen.taskbar_catch_up_threshold, + R.dimen.taskbar_catch_up_threshold_mult); + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java index 065d1117c8..916b1e6c43 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java @@ -26,8 +26,8 @@ import android.animation.ValueAnimator; import androidx.annotation.Nullable; import androidx.dynamicanimation.animation.SpringForce; +import com.android.app.animation.Interpolators; import com.android.launcher3.anim.AnimatedFloat; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.SpringAnimationBuilder; import com.android.launcher3.util.DisplayController; @@ -92,6 +92,10 @@ public class TaskbarTranslationController implements TaskbarControllers.Loggable mControllers.stashedHandleViewController.setTranslationYForSwipe(transY); mControllers.taskbarViewController.setTranslationYForSwipe(transY); mControllers.taskbarDragLayerController.setTranslationYForSwipe(transY); + mControllers.bubbleControllers.ifPresent(controllers -> { + controllers.bubbleBarViewController.setTranslationYForSwipe(transY); + controllers.bubbleStashedHandleViewController.setTranslationYForSwipe(transY); + }); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index 7154731ee5..6fad655687 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -40,8 +40,11 @@ import com.android.quickstep.util.GroupTask; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.quickstep.views.TaskView.TaskIdAttributeContainer; +import com.android.systemui.shared.recents.model.Task; import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Collections; import java.util.stream.Stream; /** @@ -204,9 +207,10 @@ public class TaskbarUIController { return; } - recentsView.getSplitSelectController().findLastActiveTaskAndRunCallback( - splitSelectSource.itemInfo.getComponentKey(), - foundTask -> { + recentsView.getSplitSelectController().findLastActiveTasksAndRunCallback( + Collections.singletonList(splitSelectSource.itemInfo.getComponentKey()), + foundTasks -> { + @Nullable Task foundTask = foundTasks.get(0); splitSelectSource.alreadyRunningTaskId = foundTask == null ? INVALID_TASK_ID : foundTask.key.id; @@ -221,9 +225,10 @@ public class TaskbarUIController { */ public void triggerSecondAppForSplit(ItemInfoWithIcon info, Intent intent, View startingView) { RecentsView recents = getRecentsView(); - recents.getSplitSelectController().findLastActiveTaskAndRunCallback( - info.getComponentKey(), - foundTask -> { + recents.getSplitSelectController().findLastActiveTasksAndRunCallback( + Collections.singletonList(info.getComponentKey()), + foundTasks -> { + @Nullable Task foundTask = foundTasks.get(0); if (foundTask != null) { TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id); // TODO (b/266482558): This additional null check is needed because there diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index bf3b932424..fa5a1ae6fa 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -18,6 +18,7 @@ package com.android.launcher3.taskbar; import static android.content.pm.PackageManager.FEATURE_PC; import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED; +import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES; import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR; import android.content.Context; @@ -87,7 +88,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar private @Nullable IconButtonView mAllAppsButton; // Only non-null when device supports having an All Apps button. - private @Nullable View mTaskbarDivider; + private @Nullable IconButtonView mTaskbarDivider; private View mQsb; @@ -157,8 +158,12 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar mActivityContext.getColor(R.color.all_apps_button_color)); if (FeatureFlags.ENABLE_TASKBAR_PINNING.get()) { - mTaskbarDivider = LayoutInflater.from(context).inflate(R.layout.taskbar_divider, + mTaskbarDivider = (IconButtonView) LayoutInflater.from(context).inflate( + R.layout.taskbar_divider, this, false); + mTaskbarDivider.setIconDrawable( + resources.getDrawable(R.drawable.taskbar_divider_button)); + mTaskbarDivider.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding); } } @@ -319,6 +324,9 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar } } setClickAndLongClickListenersForIcon(hotseatView); + if (ENABLE_CURSOR_HOVER_STATES.get()) { + setHoverListenerForIcon(hotseatView); + } nextViewIndex++; } // Remove remaining views @@ -366,16 +374,18 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar icon.setOnLongClickListener(mIconLongClickListener); } + /** + * Sets OnHoverListener for the given view. + */ + private void setHoverListenerForIcon(View icon) { + icon.setOnHoverListener(mControllerCallbacks.getIconOnHoverListener(icon)); + } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { int count = getChildCount(); DeviceProfile deviceProfile = mActivityContext.getDeviceProfile(); int spaceNeeded = getIconLayoutWidth(); - // We are removing the margin from taskbar divider item in taskbar, - // so remove it from spacing also. - if (FeatureFlags.ENABLE_TASKBAR_PINNING.get() && count > 1) { - spaceNeeded -= mIconTouchSize; - } int navSpaceNeeded = deviceProfile.hotseatBarEndOffset; boolean layoutRtl = isLayoutRtl(); int centerAlignIconEnd = right - (right - left - spaceNeeded) / 2; @@ -496,7 +506,15 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar if (deviceProfile.isQsbInline) { countExcludingQsb--; } - return countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize); + int iconLayoutBoundsWidth = + countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize); + + if (FeatureFlags.ENABLE_TASKBAR_PINNING.get() && countExcludingQsb > 1) { + // We are removing 4 * mItemMarginLeftRight as there should be no space between + // All Apps icon, divider icon, and first app icon in taskbar + iconLayoutBoundsWidth -= mItemMarginLeftRight * 4; + } + return iconLayoutBoundsWidth; } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 4abd9957b2..4614e8ddc7 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.taskbar; +import static com.android.app.animation.Interpolators.FINAL_FRAME; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; @@ -22,9 +24,8 @@ import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.anim.AnimatedFloat.VALUE; import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; -import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP; +import static com.android.launcher3.taskbar.TaskbarManager.isPhoneButtonNavMode; import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode; import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE; import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_ALIGNMENT_ANIM; @@ -45,6 +46,7 @@ import androidx.annotation.Nullable; import androidx.core.graphics.ColorUtils; import androidx.core.view.OneShotPreDrawListener; +import com.android.app.animation.Interpolators; import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; @@ -53,7 +55,6 @@ import com.android.launcher3.Utilities; import com.android.launcher3.anim.AlphaUpdateListener; import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.RevealOutlineAnimation; import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; @@ -121,7 +122,9 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar private AnimatorPlaybackController mIconAlignControllerLazy = null; private Runnable mOnControllerPreCreateCallback = NO_OP; + // Stored here as signals to determine if the mIconAlignController needs to be recreated. private boolean mIsHotseatIconOnTopWhenAligned; + private boolean mIsStashed; private final DeviceProfile.OnDeviceProfileChangeListener mDeviceProfileChangeListener = dp -> commitRunningAppsToUI(); @@ -133,7 +136,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar mTaskbarView = taskbarView; mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, NUM_ALPHA_CHANNELS); mTaskbarIconAlpha.setUpdateVisibility(true); - mModelCallbacks = new TaskbarModelCallbacks(activity, mTaskbarView); + mModelCallbacks = TaskbarModelCallbacksFactory.newInstance(mActivity) + .create(mActivity, mTaskbarView); mTaskbarBottomMargin = activity.getDeviceProfile().taskbarBottomMargin; mStashedHandleHeight = activity.getResources() .getDimensionPixelSize(R.dimen.taskbar_stashed_handle_height); @@ -170,6 +174,13 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar .getTaskbarNavButtonTranslationYForInAppDisplay(); mActivity.addOnDeviceProfileChangeListener(mDeviceProfileChangeListener); + + if (TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) { + // This gets modified in NavbarButtonsViewController, but the initial value it reads + // may be incorrect since it's state gets destroyed on taskbar recreate, so reset here + mTaskbarIconAlpha.get(ALPHA_INDEX_SMALL_SCREEN) + .animateToValue(isPhoneButtonNavMode(mActivity) ? 0 : 1).start(); + } } /** @@ -426,10 +437,13 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) { boolean isHotseatIconOnTopWhenAligned = mControllers.uiController.isHotseatIconOnTopWhenAligned(); - // When mIsHotseatIconOnTopWhenAligned changes, animation needs to be re-created. + boolean isStashed = mControllers.taskbarStashController.isStashed(); + // Re-create animation when mIsHotseatIconOnTopWhenAligned or mIsStashed changes. if (mIconAlignControllerLazy == null - || mIsHotseatIconOnTopWhenAligned != isHotseatIconOnTopWhenAligned) { + || mIsHotseatIconOnTopWhenAligned != isHotseatIconOnTopWhenAligned + || mIsStashed != isStashed) { mIsHotseatIconOnTopWhenAligned = isHotseatIconOnTopWhenAligned; + mIsStashed = isStashed; mIconAlignControllerLazy = createIconAlignmentController(launcherDp); } mIconAlignControllerLazy.setPlayFraction(alignmentRatio); @@ -443,8 +457,13 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar * Creates an animation for aligning the Taskbar icons with the provided Launcher device profile */ private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) { - mOnControllerPreCreateCallback.run(); PendingAnimation setter = new PendingAnimation(100); + if (TaskbarManager.isPhoneButtonNavMode(mActivity)) { + // No animation for icons in small-screen + return setter.createPlaybackController(); + } + + mOnControllerPreCreateCallback.run(); DeviceProfile taskbarDp = mActivity.getDeviceProfile(); Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity); float scaleUp = ((float) launcherDp.iconSizePx) / taskbarDp.taskbarIconSize; @@ -486,7 +505,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar || (isTaskbarDividerView && FeatureFlags.ENABLE_TASKBAR_PINNING.get())) { if (!isToHome && mIsHotseatIconOnTopWhenAligned - && mControllers.taskbarStashController.isStashed()) { + && mIsStashed) { // Prevent All Apps icon from appearing when going from hotseat to nav handle. setter.setViewAlpha(child, 0, Interpolators.clampToProgress(LINEAR, 0f, 0f)); } else { @@ -676,6 +695,11 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar .updateAndAnimateIsManuallyStashedInApp(true); } + /** Gets the hover listener for the provided icon view. */ + public View.OnHoverListener getIconOnHoverListener(View icon) { + return new TaskbarHoverToolTipController(mActivity, mTaskbarView, icon); + } + /** * Get the first chance to handle TaskbarView#onTouchEvent, and return whether we want to * consume the touch so TaskbarView treats it as an ACTION_CANCEL. diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java index b4b83f6f24..5d91acd3e1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java @@ -47,9 +47,9 @@ public class TaskbarAllAppsContainerView extends } @Override - protected View inflateSearchBox() { + protected View inflateSearchBar() { if (isSearchSupported()) { - return super.inflateSearchBox(); + return super.inflateSearchBar(); } // Remove top padding of header, since we do not have any search diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java index e0a502bdb2..d786d94149 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java @@ -15,11 +15,17 @@ */ package com.android.launcher3.taskbar.allapps; +import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY; + +import android.view.View; + +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.launcher3.R; import com.android.launcher3.appprediction.PredictionRowView; +import com.android.launcher3.dragndrop.DragOptions.PreDragCondition; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.taskbar.TaskbarControllers; @@ -30,7 +36,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Predicate; - /** * Handles the all apps overlay window initialization, updates, and its data. * <p> @@ -45,13 +50,16 @@ import java.util.function.Predicate; public final class TaskbarAllAppsController { private TaskbarControllers mControllers; + private @Nullable TaskbarOverlayContext mOverlayContext; private @Nullable TaskbarAllAppsSlideInView mSlideInView; private @Nullable TaskbarAllAppsContainerView mAppsView; + private @Nullable TaskbarSearchSessionController mSearchSessionController; // Application data models. - private AppInfo[] mApps; + private @NonNull AppInfo[] mApps = EMPTY_ARRAY; private int mAppsModelFlags; - private List<ItemInfo> mPredictedApps; + private @NonNull List<ItemInfo> mPredictedApps = Collections.emptyList(); + private @Nullable List<ItemInfo> mZeroStateSearchSuggestions; private boolean mDisallowGlobalDrag; private boolean mDisallowLongClick; @@ -70,9 +78,14 @@ public final class TaskbarAllAppsController { } } + /** Clean up the controller. */ + public void onDestroy() { + cleanUpOverlay(); + } + /** Updates the current {@link AppInfo} instances. */ - public void setApps(AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map) { - mApps = apps; + public void setApps(@Nullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map) { + mApps = apps == null ? EMPTY_ARRAY : apps; mAppsModelFlags = flags; mPackageUserKeytoUidMap = map; if (mAppsView != null) { @@ -96,6 +109,14 @@ public final class TaskbarAllAppsController { .findFixedRowByType(PredictionRowView.class) .setPredictedApps(mPredictedApps); } + if (mSearchSessionController != null) { + mSearchSessionController.setZeroStatePredictedItems(predictedApps); + } + } + + /** Updates the current search suggestions. */ + public void setZeroStateSearchSuggestions(List<ItemInfo> zeroStateSearchSuggestions) { + mZeroStateSearchSuggestions = zeroStateSearchSuggestions; } /** Updates the current notification dots. */ @@ -127,20 +148,28 @@ public final class TaskbarAllAppsController { // to catch invalid states. mControllers.getSharedState().allAppsVisible = true; - TaskbarOverlayContext overlayContext = - mControllers.taskbarOverlayController.requestWindow(); - mSlideInView = (TaskbarAllAppsSlideInView) overlayContext.getLayoutInflater().inflate( - R.layout.taskbar_all_apps, overlayContext.getDragLayer(), false); + mOverlayContext = mControllers.taskbarOverlayController.requestWindow(); + + // Initialize search session for All Apps. + mSearchSessionController = TaskbarSearchSessionController.newInstance(mOverlayContext); + mOverlayContext.setSearchSessionController(mSearchSessionController); + mSearchSessionController.setZeroStatePredictedItems(mPredictedApps); + if (mZeroStateSearchSuggestions != null) { + mSearchSessionController.setZeroStateSearchSuggestions(mZeroStateSearchSuggestions); + } + mSearchSessionController.startLifecycle(); + + mSlideInView = (TaskbarAllAppsSlideInView) mOverlayContext.getLayoutInflater().inflate( + R.layout.taskbar_all_apps_sheet, mOverlayContext.getDragLayer(), false); mSlideInView.addOnCloseListener(() -> { mControllers.getSharedState().allAppsVisible = false; - mSlideInView = null; - mAppsView = null; + cleanUpOverlay(); }); TaskbarAllAppsViewController viewController = new TaskbarAllAppsViewController( - overlayContext, mSlideInView, mControllers); + mOverlayContext, mSlideInView, mControllers, mSearchSessionController); viewController.show(animate); - mAppsView = overlayContext.getAppsView(); + mAppsView = mOverlayContext.getAppsView(); mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags, mPackageUserKeytoUidMap); mAppsView.getFloatingHeaderView() .findFixedRowByType(PredictionRowView.class) @@ -149,8 +178,28 @@ public final class TaskbarAllAppsController { // Create a shared drag layer between taskbar and taskbarAllApps so that when dragging // starts and taskbarAllApps can close, but the drag layer that the view is being dragged in // doesn't also close - overlayContext.getDragController().setDisallowGlobalDrag(mDisallowGlobalDrag); - overlayContext.getDragController().setDisallowLongClick(mDisallowLongClick); + mOverlayContext.getDragController().setDisallowGlobalDrag(mDisallowGlobalDrag); + mOverlayContext.getDragController().setDisallowLongClick(mDisallowLongClick); + } + + private void cleanUpOverlay() { + // Floating search bar is added to the drag layer in ActivityAllAppsContainerView onAttach; + // removed here as this is a special case that we remove the all apps panel. + if (mAppsView != null && mOverlayContext != null + && mAppsView.getSearchUiDelegate().isSearchBarFloating()) { + mOverlayContext.getDragLayer().removeView(mAppsView.getSearchView()); + mAppsView.getSearchUiDelegate().onDestroySearchBar(); + } + if (mSearchSessionController != null) { + mSearchSessionController.onDestroy(); + mSearchSessionController = null; + } + if (mOverlayContext != null) { + mOverlayContext.setSearchSessionController(null); + mOverlayContext = null; + } + mSlideInView = null; + mAppsView = null; } @VisibleForTesting @@ -164,4 +213,12 @@ public final class TaskbarAllAppsController { // Allow null-pointer since this should only be null if the apps view is not showing. return mAppsView.getActiveRecyclerView().computeVerticalScrollOffset(); } + + /** @see TaskbarSearchSessionController#createPreDragConditionForSearch(View) */ + @Nullable + public PreDragCondition createPreDragConditionForSearch(View view) { + return mSearchSessionController != null + ? mSearchSessionController.createPreDragConditionForSearch(view) + : null; + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java index cfa1027dcc..001c3bcfc0 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java @@ -15,20 +15,27 @@ */ package com.android.launcher3.taskbar.allapps; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED; +import static com.android.app.animation.Interpolators.EMPHASIZED; -import android.animation.PropertyValuesHolder; +import android.animation.Animator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; import android.util.AttributeSet; import android.view.MotionEvent; +import android.view.View; import android.view.animation.Interpolator; import android.window.OnBackInvokedDispatcher; +import androidx.annotation.Nullable; + import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.R; +import com.android.launcher3.anim.AnimatorListeners; +import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.taskbar.allapps.TaskbarAllAppsViewController.TaskbarAllAppsCallbacks; import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext; @@ -37,8 +44,11 @@ import com.android.launcher3.views.AbstractSlideInView; /** Wrapper for taskbar all apps with slide-in behavior. */ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverlayContext> implements Insettable, DeviceProfile.OnDeviceProfileChangeListener { + private final Handler mHandler; + private TaskbarAllAppsContainerView mAppsView; private float mShiftRange; + private @Nullable Runnable mShowOnFullyAttachedToWindowRunnable; // Initialized in init. private TaskbarAllAppsCallbacks mAllAppsCallbacks; @@ -50,6 +60,7 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverla public TaskbarAllAppsSlideInView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); + mHandler = new Handler(Looper.myLooper()); } void init(TaskbarAllAppsCallbacks callbacks) { @@ -58,20 +69,51 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverla /** Opens the all apps view. */ void show(boolean animate) { - if (mIsOpen || mOpenCloseAnimator.isRunning()) { + if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) { return; } mIsOpen = true; + + addOnAttachStateChangeListener(new OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + removeOnAttachStateChangeListener(this); + // Wait for view and its descendants to be fully attached before starting open. + mShowOnFullyAttachedToWindowRunnable = () -> showOnFullyAttachedToWindow(animate); + mHandler.post(mShowOnFullyAttachedToWindowRunnable); + } + + @Override + public void onViewDetachedFromWindow(View v) { + removeOnAttachStateChangeListener(this); + } + }); attachToContainer(); + } - if (animate) { - mOpenCloseAnimator.setValues( - PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); - mOpenCloseAnimator.setInterpolator(EMPHASIZED); - mOpenCloseAnimator.setDuration(mAllAppsCallbacks.getOpenDuration()).start(); - } else { + private void showOnFullyAttachedToWindow(boolean animate) { + mAllAppsCallbacks.onAllAppsTransitionStart(true); + if (!animate) { + mAllAppsCallbacks.onAllAppsTransitionEnd(true); mTranslationShift = TRANSLATION_SHIFT_OPENED; + return; } + + setUpOpenAnimation(mAllAppsCallbacks.getOpenDuration()); + Animator animator = mOpenCloseAnimation.getAnimationPlayer(); + animator.setInterpolator(EMPHASIZED); + animator.addListener(AnimatorListeners.forEndCallback(() -> { + if (mIsOpen) { + mAllAppsCallbacks.onAllAppsTransitionEnd(true); + } + })); + animator.start(); + } + + @Override + protected void onOpenCloseAnimationPending(PendingAnimation animation) { + mAllAppsCallbacks.onAllAppsAnimationPending( + animation, mToTranslationShift == TRANSLATION_SHIFT_OPENED); } /** The apps container inside this view. */ @@ -81,10 +123,23 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverla @Override protected void handleClose(boolean animate) { + if (mShowOnFullyAttachedToWindowRunnable != null) { + mHandler.removeCallbacks(mShowOnFullyAttachedToWindowRunnable); + mShowOnFullyAttachedToWindowRunnable = null; + } + if (mIsOpen) { + mAllAppsCallbacks.onAllAppsTransitionStart(false); + } handleClose(animate, mAllAppsCallbacks.getCloseDuration()); } @Override + protected void onCloseComplete() { + mAllAppsCallbacks.onAllAppsTransitionEnd(false); + super.onCloseComplete(); + } + + @Override protected Interpolator getIdleInterpolator() { return EMPHASIZED; } @@ -194,4 +249,11 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverla protected boolean isEventOverContent(MotionEvent ev) { return getPopupContainer().isEventOverView(mAppsView.getVisibleContainerView(), ev); } + + @Override + public void onBackInvoked() { + if (!mAllAppsCallbacks.handleSearchBackInvoked()) { + super.onBackInvoked(); + } + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java index 01342affb6..85633e9037 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java @@ -19,8 +19,9 @@ import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_ import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.allapps.AllAppsTransitionListener; +import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.appprediction.AppsDividerView; -import com.android.launcher3.appprediction.PredictionRowView; import com.android.launcher3.taskbar.NavbarButtonsViewController; import com.android.launcher3.taskbar.TaskbarControllers; import com.android.launcher3.taskbar.TaskbarStashController; @@ -44,7 +45,8 @@ final class TaskbarAllAppsViewController { TaskbarAllAppsViewController( TaskbarOverlayContext context, TaskbarAllAppsSlideInView slideInView, - TaskbarControllers taskbarControllers) { + TaskbarControllers taskbarControllers, + TaskbarSearchSessionController searchSessionController) { mContext = context; mSlideInView = slideInView; @@ -53,8 +55,7 @@ final class TaskbarAllAppsViewController { mNavbarButtonsViewController = taskbarControllers.navbarButtonsViewController; mOverlayController = taskbarControllers.taskbarOverlayController; - mSlideInView.init(new TaskbarAllAppsCallbacks()); - setUpIconLongClick(); + mSlideInView.init(new TaskbarAllAppsCallbacks(searchSessionController)); setUpAppDivider(); setUpTaskbarStashing(); } @@ -69,15 +70,6 @@ final class TaskbarAllAppsViewController { mSlideInView.close(animate); } - private void setUpIconLongClick() { - mAppsView.setOnIconLongClickListener( - mContext.getDragController()::startDragOnLongClick); - mAppsView.getFloatingHeaderView() - .findFixedRowByType(PredictionRowView.class) - .setOnIconLongClickListener( - mContext.getDragController()::startDragOnLongClick); - } - private void setUpAppDivider() { mAppsView.getFloatingHeaderView() .findFixedRowByType(AppsDividerView.class) @@ -105,7 +97,13 @@ final class TaskbarAllAppsViewController { }); } - class TaskbarAllAppsCallbacks { + class TaskbarAllAppsCallbacks implements AllAppsTransitionListener { + private final TaskbarSearchSessionController mSearchSessionController; + + private TaskbarAllAppsCallbacks(TaskbarSearchSessionController searchSessionController) { + mSearchSessionController = searchSessionController; + } + int getOpenDuration() { return mOverlayController.getOpenDuration(); } @@ -113,5 +111,24 @@ final class TaskbarAllAppsViewController { int getCloseDuration() { return mOverlayController.getCloseDuration(); } + + @Override + public void onAllAppsTransitionStart(boolean toAllApps) { + mSearchSessionController.onAllAppsTransitionStart(toAllApps); + } + + @Override + public void onAllAppsTransitionEnd(boolean toAllApps) { + mSearchSessionController.onAllAppsTransitionEnd(toAllApps); + } + + /** Invoked on back press, returning {@code true} if the search session handled it. */ + boolean handleSearchBackInvoked() { + return mSearchSessionController.handleBackInvoked(); + } + + void onAllAppsAnimationPending(PendingAnimation animation, boolean toAllApps) { + mSearchSessionController.onAllAppsAnimationPending(animation, toAllApps); + } } } diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt new file mode 100644 index 0000000000..8a2041fc34 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt @@ -0,0 +1,70 @@ +/* + * 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.launcher3.taskbar.allapps + +import android.content.Context +import android.view.View +import com.android.launcher3.R +import com.android.launcher3.allapps.AllAppsTransitionListener +import com.android.launcher3.anim.PendingAnimation +import com.android.launcher3.config.FeatureFlags +import com.android.launcher3.dragndrop.DragOptions.PreDragCondition +import com.android.launcher3.model.data.ItemInfo +import com.android.launcher3.util.ResourceBasedOverride +import com.android.launcher3.util.ResourceBasedOverride.Overrides + +/** Stub for managing the Taskbar search session. */ +open class TaskbarSearchSessionController : ResourceBasedOverride, AllAppsTransitionListener { + + /** Start the search session lifecycle. */ + open fun startLifecycle() = Unit + + /** Destroy the search session. */ + open fun onDestroy() = Unit + + /** Updates the predicted items shown in the zero-state. */ + open fun setZeroStatePredictedItems(items: List<ItemInfo>) = Unit + + /** Updates the search suggestions shown in the zero-state. */ + open fun setZeroStateSearchSuggestions(items: List<ItemInfo>) = Unit + + override fun onAllAppsTransitionStart(toAllApps: Boolean) = Unit + + override fun onAllAppsTransitionEnd(toAllApps: Boolean) = Unit + + /** Creates a [PreDragCondition] for [view], if it is a search result that requires one. */ + open fun createPreDragConditionForSearch(view: View): PreDragCondition? = null + + open fun handleBackInvoked(): Boolean = false + + open fun onAllAppsAnimationPending(animation: PendingAnimation, toAllApps: Boolean) = Unit + + companion object { + @JvmStatic + fun newInstance(context: Context): TaskbarSearchSessionController { + if (!FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) { + return TaskbarSearchSessionController() + } + + return Overrides.getObject( + TaskbarSearchSessionController::class.java, + context, + R.string.taskbar_search_session_controller_class, + ) + } + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt index 7397159f42..1e3f4f13f2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt @@ -21,10 +21,10 @@ import android.graphics.ColorFilter import android.graphics.Paint import android.graphics.drawable.Drawable import android.graphics.drawable.ShapeDrawable +import com.android.app.animation.Interpolators import com.android.launcher3.R import com.android.launcher3.Utilities import com.android.launcher3.Utilities.mapToRange -import com.android.launcher3.anim.Interpolators import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound import com.android.launcher3.taskbar.TaskbarActivityContext import com.android.wm.shell.common.TriangleShape @@ -48,6 +48,8 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun private var showingArrow: Boolean = false private var arrowDrawable: ShapeDrawable + var width: Float = 0f + init { paint.color = context.getColor(R.color.taskbar_background) paint.flags = Paint.ANTI_ALIAS_FLAG @@ -59,8 +61,11 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun pointerSize = res.getDimension(R.dimen.bubblebar_pointer_size) shadowAlpha = - if (Utilities.isDarkTheme(context)) DARK_THEME_SHADOW_ALPHA - else LIGHT_THEME_SHADOW_ALPHA + if (Utilities.isDarkTheme(context)) { + DARK_THEME_SHADOW_ALPHA + } else { + LIGHT_THEME_SHADOW_ALPHA + } arrowDrawable = ShapeDrawable(TriangleShape.create(pointerSize, pointerSize, /* pointUp= */ true)) @@ -102,7 +107,7 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun // Draw background. val radius = backgroundHeight / 2f canvas.drawRoundRect( - 0f, + canvas.width.toFloat() - width, 0f, canvas.width.toFloat(), canvas.height.toFloat(), @@ -132,4 +137,8 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun override fun setColorFilter(colorFilter: ColorFilter?) { paint.colorFilter = colorFilter } + + fun setArrowAlpha(alpha: Int) { + arrowDrawable.paint.alpha = alpha + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java index 6d196924b3..24db380553 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java @@ -31,18 +31,25 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_Q import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED; +import static java.lang.Math.abs; + import android.annotation.BinderThread; import android.annotation.Nullable; +import android.app.Notification; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; +import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Path; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.InsetDrawable; import android.os.Bundle; import android.os.SystemProperties; import android.os.UserHandle; @@ -51,6 +58,8 @@ import android.util.Log; import android.util.PathParser; import android.view.LayoutInflater; +import androidx.appcompat.content.res.AppCompatResources; + import com.android.internal.graphics.ColorUtils; import com.android.launcher3.R; import com.android.launcher3.icons.BitmapInfo; @@ -112,8 +121,10 @@ public class BubbleBarController extends IBubblesListener.Stub { private final Executor mMainExecutor; private final LauncherApps mLauncherApps; private final BubbleIconFactory mIconFactory; + private final SystemUiProxy mSystemUiProxy; - private BubbleBarBubble mSelectedBubble; + private BubbleBarItem mSelectedBubble; + private BubbleBarOverflow mOverflowBubble; private BubbleBarViewController mBubbleBarViewController; private BubbleStashController mBubbleStashController; @@ -152,8 +163,10 @@ public class BubbleBarController extends IBubblesListener.Stub { mContext = context; mBarView = bubbleView; // Need the view for inflating bubble views. + mSystemUiProxy = SystemUiProxy.INSTANCE.get(context); + if (BUBBLE_BAR_ENABLED) { - SystemUiProxy.INSTANCE.get(context).setBubblesListener(this); + mSystemUiProxy.setBubblesListener(this); } mMainExecutor = MAIN_EXECUTOR; mLauncherApps = context.getSystemService(LauncherApps.class); @@ -166,7 +179,7 @@ public class BubbleBarController extends IBubblesListener.Stub { } public void onDestroy() { - SystemUiProxy.INSTANCE.get(mContext).setBubblesListener(null); + mSystemUiProxy.setBubblesListener(null); } public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) { @@ -177,10 +190,32 @@ public class BubbleBarController extends IBubblesListener.Stub { bubbleControllers.runAfterInit(() -> { mBubbleBarViewController.setHiddenForBubbles(!BUBBLE_BAR_ENABLED); mBubbleStashedHandleViewController.setHiddenForBubbles(!BUBBLE_BAR_ENABLED); + mBubbleBarViewController.setUpdateSelectedBubbleAfterCollapse( + key -> setSelectedBubble(mBubbles.get(key))); }); } /** + * Creates and adds the overflow bubble to the bubble bar if it hasn't been created yet. + * + * <p>This should be called on the {@link #BUBBLE_STATE_EXECUTOR} executor to avoid inflating + * the overflow multiple times. + */ + private void createAndAddOverflowIfNeeded() { + if (mOverflowBubble == null) { + BubbleBarOverflow overflow = createOverflow(mContext); + mMainExecutor.execute(() -> { + // we're on the main executor now, so check that the overflow hasn't been created + // again to avoid races. + if (mOverflowBubble == null) { + mBubbleBarViewController.addBubble(overflow); + mOverflowBubble = overflow; + } + }); + } + } + + /** * Updates the bubble bar, handle bar, and stash controllers based on sysui state flags. */ public void updateStateForSysuiFlags(int flags) { @@ -209,18 +244,23 @@ public class BubbleBarController extends IBubblesListener.Stub { || !update.currentBubbleList.isEmpty()) { // We have bubbles to load BUBBLE_STATE_EXECUTOR.execute(() -> { + createAndAddOverflowIfNeeded(); if (update.addedBubble != null) { - viewUpdate.addedBubble = populateBubble(update.addedBubble, mContext, mBarView); + viewUpdate.addedBubble = populateBubble(mContext, update.addedBubble, mBarView, + null /* existingBubble */); } if (update.updatedBubble != null) { + BubbleBarBubble existingBubble = mBubbles.get(update.updatedBubble.getKey()); viewUpdate.updatedBubble = - populateBubble(update.updatedBubble, mContext, mBarView); + populateBubble(mContext, update.updatedBubble, mBarView, + existingBubble); } if (update.currentBubbleList != null && !update.currentBubbleList.isEmpty()) { List<BubbleBarBubble> currentBubbles = new ArrayList<>(); for (int i = 0; i < update.currentBubbleList.size(); i++) { BubbleBarBubble b = - populateBubble(update.currentBubbleList.get(i), mContext, mBarView); + populateBubble(mContext, update.currentBubbleList.get(i), mBarView, + null /* existingBubble */); currentBubbles.add(b); } viewUpdate.currentBubbles = currentBubbles; @@ -237,6 +277,7 @@ public class BubbleBarController extends IBubblesListener.Stub { private void applyViewChanges(BubbleBarViewUpdate update) { final boolean isCollapsed = (update.expandedChanged && !update.expanded) || (!update.expandedChanged && !mBubbleBarViewController.isExpanded()); + BubbleBarItem previouslySelectedBubble = mSelectedBubble; BubbleBarBubble bubbleToSelect = null; if (!update.removedBubbles.isEmpty()) { for (int i = 0; i < update.removedBubbles.size(); i++) { @@ -281,10 +322,17 @@ public class BubbleBarController extends IBubblesListener.Stub { mBubbleBarViewController.setHiddenForBubbles(mBubbles.isEmpty()); mBubbleStashedHandleViewController.setHiddenForBubbles(mBubbles.isEmpty()); + if (mBubbles.isEmpty()) { + // all bubbles were removed. clear the selected bubble + mSelectedBubble = null; + } + if (update.updatedBubble != null) { - // TODO: (b/269670235) handle updates: - // (1) if content / icons change -- requires reload & add back in place - // (2) if showing update dot changes -- tell the view to hide / show the dot + // Updates mean the dot state may have changed; any other changes were updated in + // the populateBubble step. + BubbleBarBubble bb = mBubbles.get(update.updatedBubble.getKey()); + // If we're not stashed, we're visible so animate + bb.getView().updateDotVisibility(!mBubbleStashController.isStashed() /* animate */); } if (update.bubbleKeysInOrder != null && !update.bubbleKeysInOrder.isEmpty()) { // Create the new list @@ -301,8 +349,8 @@ public class BubbleBarController extends IBubblesListener.Stub { // TODO: (b/273316505) handle suppression } if (update.selectedBubbleKey != null) { - if (mSelectedBubble != null - && !update.selectedBubbleKey.equals(mSelectedBubble.getKey())) { + if (mSelectedBubble == null + || !update.selectedBubbleKey.equals(mSelectedBubble.getKey())) { BubbleBarBubble newlySelected = mBubbles.get(update.selectedBubbleKey); if (newlySelected != null) { bubbleToSelect = newlySelected; @@ -314,7 +362,11 @@ public class BubbleBarController extends IBubblesListener.Stub { } if (bubbleToSelect != null) { setSelectedBubble(bubbleToSelect); + if (previouslySelectedBubble == null) { + mBubbleStashController.animateToInitialState(update.expanded); + } } + if (update.expandedChanged) { if (update.expanded != mBubbleBarViewController.isExpanded()) { mBubbleBarViewController.setExpandedFromSysui(update.expanded); @@ -324,12 +376,38 @@ public class BubbleBarController extends IBubblesListener.Stub { } } + /** Tells WMShell to show the currently selected bubble. */ + public void showSelectedBubble() { + if (getSelectedBubbleKey() != null) { + if (mSelectedBubble instanceof BubbleBarBubble) { + // Because we've visited this bubble, we should suppress the notification. + // This is updated on WMShell side when we show the bubble, but that update isn't + // passed to launcher, instead we apply it directly here. + BubbleInfo info = ((BubbleBarBubble) mSelectedBubble).getInfo(); + info.setFlags( + info.getFlags() | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION); + mSelectedBubble.getView().updateDotVisibility(true /* animate */); + } + mSystemUiProxy.showBubble(getSelectedBubbleKey(), + getBubbleBarOffsetX(), getBubbleBarOffsetY()); + } else { + Log.w(TAG, "Trying to show the selected bubble but it's null"); + } + } + + /** Updates the currently selected bubble for launcher views and tells WMShell to show it. */ + public void showAndSelectBubble(BubbleBarItem b) { + if (DEBUG) Log.w(TAG, "showingSelectedBubble: " + b.getKey()); + setSelectedBubble(b); + showSelectedBubble(); + } + /** * Sets the bubble that should be selected. This notifies the views, it does not notify - * WMShell that the selection has changed, that should go through - * {@link SystemUiProxy#showBubble}. + * WMShell that the selection has changed, that should go through either + * {@link #showSelectedBubble()} or {@link #showAndSelectBubble(BubbleBarItem)}. */ - public void setSelectedBubble(BubbleBarBubble b) { + private void setSelectedBubble(BubbleBarItem b) { if (!Objects.equals(b, mSelectedBubble)) { if (DEBUG) Log.w(TAG, "selectingBubble: " + b.getKey()); mSelectedBubble = b; @@ -353,7 +431,8 @@ public class BubbleBarController extends IBubblesListener.Stub { // @Nullable - private BubbleBarBubble populateBubble(BubbleInfo b, Context context, BubbleBarView bbv) { + private BubbleBarBubble populateBubble(Context context, BubbleInfo b, BubbleBarView bbv, + @Nullable BubbleBarBubble existingBubble) { String appName; Bitmap badgeBitmap; Bitmap bubbleBitmap; @@ -422,16 +501,68 @@ public class BubbleBarController extends IBubblesListener.Stub { iconPath.transform(matrix); dotPath = iconPath; dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color, - Color.WHITE, WHITE_SCRIM_ALPHA); + Color.WHITE, WHITE_SCRIM_ALPHA / 255f); + if (existingBubble == null) { + LayoutInflater inflater = LayoutInflater.from(context); + BubbleView bubbleView = (BubbleView) inflater.inflate( + R.layout.bubblebar_item_view, bbv, false /* attachToRoot */); + BubbleBarBubble bubble = new BubbleBarBubble(b, bubbleView, + badgeBitmap, bubbleBitmap, dotColor, dotPath, appName); + bubbleView.setBubble(bubble); + return bubble; + } else { + // If we already have a bubble (so it already has an inflated view), update it. + existingBubble.setInfo(b); + existingBubble.setBadge(badgeBitmap); + existingBubble.setIcon(bubbleBitmap); + existingBubble.setDotColor(dotColor); + existingBubble.setDotPath(dotPath); + existingBubble.setAppName(appName); + return existingBubble; + } + } + + private BubbleBarOverflow createOverflow(Context context) { + Bitmap bitmap = createOverflowBitmap(context); LayoutInflater inflater = LayoutInflater.from(context); BubbleView bubbleView = (BubbleView) inflater.inflate( - R.layout.bubblebar_item_view, bbv, false /* attachToRoot */); + R.layout.bubblebar_item_view, mBarView, false /* attachToRoot */); + BubbleBarOverflow overflow = new BubbleBarOverflow(bubbleView); + bubbleView.setOverflow(overflow, bitmap); + return overflow; + } + + private Bitmap createOverflowBitmap(Context context) { + Drawable iconDrawable = AppCompatResources.getDrawable(mContext, + R.drawable.bubble_ic_overflow_button); + + final TypedArray ta = mContext.obtainStyledAttributes( + new int[]{ + com.android.internal.R.attr.materialColorOnPrimaryFixed, + com.android.internal.R.attr.materialColorPrimaryFixed + }); + int overflowIconColor = ta.getColor(0, Color.WHITE); + int overflowBackgroundColor = ta.getColor(1, Color.BLACK); + ta.recycle(); + + iconDrawable.setTint(overflowIconColor); + + int inset = context.getResources().getDimensionPixelSize(R.dimen.bubblebar_overflow_inset); + Drawable foreground = new InsetDrawable(iconDrawable, inset); + Drawable drawable = new AdaptiveIconDrawable(new ColorDrawable(overflowBackgroundColor), + foreground); + + return mIconFactory.createBadgedIconBitmap(drawable).icon; + } + + private int getBubbleBarOffsetY() { + final int translation = (int) abs(mBubbleStashController.getBubbleBarTranslationY()); + return translation + mBarView.getHeight(); + } - BubbleBarBubble bubble = new BubbleBarBubble(b, bubbleView, - badgeBitmap, bubbleBitmap, dotColor, dotPath, appName); - bubbleView.setBubble(bubble); - return bubble; + private int getBubbleBarOffsetX() { + return mBarView.getWidth() + mBarView.getHorizontalMargin(); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubble.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarItem.kt index 3cd5f75934..43e21f4085 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubble.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarItem.kt @@ -19,16 +19,19 @@ import android.graphics.Bitmap import android.graphics.Path import com.android.wm.shell.common.bubbles.BubbleInfo +/** An entity in the bubble bar. */ +sealed class BubbleBarItem(open var key: String, open var view: BubbleView) + /** Contains state info about a bubble in the bubble bar as well as presentation information. */ data class BubbleBarBubble( - val info: BubbleInfo, - val view: BubbleView, - val badge: Bitmap, - val icon: Bitmap, - val dotColor: Int, - val dotPath: Path, - val appName: String -) { + var info: BubbleInfo, + override var view: BubbleView, + var badge: Bitmap, + var icon: Bitmap, + var dotColor: Int, + var dotPath: Path, + var appName: String +) : BubbleBarItem(info.key, view) - val key: String = info.key -} +/** Represents the overflow bubble in the bubble bar. */ +data class BubbleBarOverflow(override var view: BubbleView) : BubbleBarItem("Overflow", view) diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index 0e1e0e1a1b..c482911ae8 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.taskbar.bubbles; +import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.Nullable; import android.content.Context; @@ -31,6 +32,7 @@ import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.launcher3.views.ActivityContext; import java.util.List; +import java.util.function.Consumer; /** * The view that holds all the bubble views. Modifying this view should happen through @@ -66,11 +68,15 @@ public class BubbleBarView extends FrameLayout { // if it's smaller than 5. private static final int MAX_BUBBLES = 5; private static final int ARROW_POSITION_ANIMATION_DURATION_MS = 200; + private static final int WIDTH_ANIMATION_DURATION_MS = 200; - private final TaskbarActivityContext mActivityContext; private final BubbleBarBackground mBubbleBarBackground; - // The current bounds of all the bubble bar. + /** + * The current bounds of all the bubble bar. Note that these bounds may not account for + * translation. The bounds should be retrieved using {@link #getBubbleBarBounds()} which + * updates the bounds and accounts for translation. + */ private final Rect mBubbleBarBounds = new Rect(); // The amount the bubbles overlap when they are stacked in the bubble bar private final float mIconOverlapAmount; @@ -89,6 +95,12 @@ public class BubbleBarView extends FrameLayout { private View.OnClickListener mOnClickListener; private final Rect mTempRect = new Rect(); + private float mRelativePivotX = 1f; + private float mRelativePivotY = 1f; + + // An animator that represents the expansion state of the bubble bar, where 0 corresponds to the + // collapsed state and 1 to the fully expanded state. + private final ValueAnimator mWidthAnimator = ValueAnimator.ofFloat(0, 1); // We don't reorder the bubbles when they are expanded as it could be jarring for the user // this runnable will be populated with any reordering of the bubbles that should be applied @@ -96,6 +108,12 @@ public class BubbleBarView extends FrameLayout { @Nullable private Runnable mReorderRunnable; + @Nullable + private Consumer<String> mUpdateSelectedBubbleAfterCollapse; + + @Nullable + private BubbleView mDraggedBubbleView; + public BubbleBarView(Context context) { this(context, null); } @@ -110,7 +128,7 @@ public class BubbleBarView extends FrameLayout { public BubbleBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mActivityContext = ActivityContext.lookupContext(context); + TaskbarActivityContext activityContext = ActivityContext.lookupContext(context); mIconOverlapAmount = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_overlap); mIconSpacing = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_spacing); @@ -118,9 +136,46 @@ public class BubbleBarView extends FrameLayout { mBubbleElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_elevation); setClipToPadding(false); - mBubbleBarBackground = new BubbleBarBackground(mActivityContext, + mBubbleBarBackground = new BubbleBarBackground(activityContext, getResources().getDimensionPixelSize(R.dimen.bubblebar_size)); setBackgroundDrawable(mBubbleBarBackground); + + mWidthAnimator.setDuration(WIDTH_ANIMATION_DURATION_MS); + mWidthAnimator.addUpdateListener(animation -> { + updateChildrenRenderNodeProperties(); + invalidate(); + }); + mWidthAnimator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationCancel(Animator animation) { + } + + @Override + public void onAnimationEnd(Animator animation) { + mBubbleBarBackground.showArrow(mIsBarExpanded); + if (!mIsBarExpanded && mReorderRunnable != null) { + mReorderRunnable.run(); + mReorderRunnable = null; + } + // If the bar was just collapsed and the overflow was the last bubble that was + // selected, set the first bubble as selected. + if (!mIsBarExpanded && mUpdateSelectedBubbleAfterCollapse != null + && mSelectedBubbleView.getBubble() instanceof BubbleBarOverflow) { + BubbleView firstBubble = (BubbleView) getChildAt(0); + mUpdateSelectedBubbleAfterCollapse.accept(firstBubble.getBubble().getKey()); + } + updateWidth(); + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + + @Override + public void onAnimationStart(Animator animation) { + mBubbleBarBackground.showArrow(true); + } + }); } @Override @@ -131,63 +186,157 @@ public class BubbleBarView extends FrameLayout { mBubbleBarBounds.right = right; mBubbleBarBounds.bottom = bottom; - // The bubble bar handle is aligned to the bottom edge of the screen so scale towards that. - setPivotX(getWidth()); - setPivotY(getHeight()); + // The bubble bar handle is aligned according to the relative pivot, + // by default it's aligned to the bottom edge of the screen so scale towards that + setPivotX(mRelativePivotX * getWidth()); + setPivotY(mRelativePivotY * getHeight()); // Position the views updateChildrenRenderNodeProperties(); } /** - * Returns the bounds of the bubble bar. + * Updates the bounds with translation that may have been applied and returns the result. */ public Rect getBubbleBarBounds() { + mBubbleBarBounds.top = getTop() + (int) getTranslationY(); + mBubbleBarBounds.bottom = getBottom() + (int) getTranslationY(); return mBubbleBarBounds; } - // TODO: (b/273592694) animate it + /** + * Set bubble bar relative pivot value for X and Y, applied as a fraction of view width/height + * respectively. If the value is not in range of 0 to 1 it will be normalized. + * @param x relative X pivot value in range 0..1 + * @param y relative Y pivot value in range 0..1 + */ + public void setRelativePivot(float x, float y) { + mRelativePivotX = Float.max(Float.min(x, 1), 0); + mRelativePivotY = Float.max(Float.min(y, 1), 0); + requestLayout(); + } + + /** + * Get current relative pivot for X axis + */ + public float getRelativePivotX() { + return mRelativePivotX; + } + + /** + * Get current relative pivot for Y axis + */ + public float getRelativePivotY() { + return mRelativePivotY; + } + + // TODO: (b/280605790) animate it @Override public void addView(View child, int index, ViewGroup.LayoutParams params) { if (getChildCount() + 1 > MAX_BUBBLES) { - removeViewInLayout(getChildAt(getChildCount() - 1)); + // the last child view is the overflow bubble and we shouldn't remove that. remove the + // second to last child view. + removeViewInLayout(getChildAt(getChildCount() - 2)); } super.addView(child, index, params); + updateWidth(); + } + + // TODO: (b/283309949) animate it + @Override + public void removeView(View view) { + super.removeView(view); + updateWidth(); + } + + private void updateWidth() { + LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); + lp.width = (int) (mIsBarExpanded ? expandedWidth() : collapsedWidth()); + setLayoutParams(lp); + } + + /** @return the horizontal margin between the bubble bar and the edge of the screen. */ + int getHorizontalMargin() { + LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); + return lp.getMarginEnd(); } /** * Updates the z order, positions, and badge visibility of the bubble views in the bar based * on the expanded state. */ - // TODO: (b/273592694) animate it private void updateChildrenRenderNodeProperties() { + final float widthState = (float) mWidthAnimator.getAnimatedValue(); + final float currentWidth = getWidth(); + final float expandedWidth = expandedWidth(); + final float collapsedWidth = collapsedWidth(); int bubbleCount = getChildCount(); final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f; + final boolean animate = getVisibility() == VISIBLE; for (int i = 0; i < bubbleCount; i++) { BubbleView bv = (BubbleView) getChildAt(i); bv.setTranslationY(ty); + + // the position of the bubble when the bar is fully expanded + final float expandedX = i * (mIconSize + mIconSpacing); + // the position of the bubble when the bar is fully collapsed + final float collapsedX = i == 0 ? 0 : mIconOverlapAmount; + if (mIsBarExpanded) { - final float tx = i * (mIconSize + mIconSpacing); - bv.setTranslationX(tx); - bv.setZ(0); - bv.showBadge(); + // where the bubble will end up when the animation ends + final float targetX = currentWidth - expandedWidth + expandedX; + bv.setTranslationX(widthState * (targetX - collapsedX) + collapsedX); + // if we're fully expanded, set the z level to 0 or to bubble elevation if dragged + if (widthState == 1f) { + bv.setZ(bv == mDraggedBubbleView ? mBubbleElevation : 0); + } + // When we're expanded, we're not stacked so we're not behind the stack + bv.setBehindStack(false, animate); + bv.setAlpha(1); } else { + final float targetX = currentWidth - collapsedWidth + collapsedX; + bv.setTranslationX(widthState * (expandedX - targetX) + targetX); bv.setZ((MAX_BUBBLES * mBubbleElevation) - i); - bv.setTranslationX(i * mIconOverlapAmount); - if (i > 0) { - bv.hideBadge(); - } else { - bv.showBadge(); + // If we're not the first bubble we're behind the stack + bv.setBehindStack(i > 0, animate); + // If we're fully collapsed, hide all bubbles except for the first 2. If there are + // only 2 bubbles, hide the second bubble as well because it's the overflow. + if (widthState == 0) { + if (i > 1) { + bv.setAlpha(0); + } else if (i == 1 && bubbleCount == 2) { + bv.setAlpha(0); + } } } } + + // update the arrow position + final float collapsedArrowPosition = arrowPositionForSelectedWhenCollapsed(); + final float expandedArrowPosition = arrowPositionForSelectedWhenExpanded(); + final float interpolatedWidth = + widthState * (expandedWidth - collapsedWidth) + collapsedWidth; + if (mIsBarExpanded) { + // when the bar is expanding, the selected bubble is always the first, so the arrow + // always shifts with the interpolated width. + final float arrowPosition = currentWidth - interpolatedWidth + collapsedArrowPosition; + mBubbleBarBackground.setArrowPosition(arrowPosition); + } else { + final float targetPosition = currentWidth - collapsedWidth + collapsedArrowPosition; + final float arrowPosition = + targetPosition + widthState * (expandedArrowPosition - targetPosition); + mBubbleBarBackground.setArrowPosition(arrowPosition); + } + + mBubbleBarBackground.setArrowAlpha((int) (255 * widthState)); + mBubbleBarBackground.setWidth(interpolatedWidth); } /** * Reorders the views to match the provided list. */ public void reorder(List<BubbleView> viewOrder) { - if (isExpanded()) { + if (isExpanded() || mWidthAnimator.isRunning()) { mReorderRunnable = () -> doReorder(viewOrder); } else { doReorder(viewOrder); @@ -199,7 +348,10 @@ public class BubbleBarView extends FrameLayout { if (!isExpanded()) { for (int i = 0; i < viewOrder.size(); i++) { View child = viewOrder.get(i); - if (child != null) { + // this child view may have already been removed so verify that it still exists + // before reordering it, otherwise it will be re-added. + int indexOfChild = indexOfChild(child); + if (child != null && indexOfChild >= 0) { removeViewInLayout(child); addViewInLayout(child, i, child.getLayoutParams()); } @@ -208,6 +360,11 @@ public class BubbleBarView extends FrameLayout { } } + public void setUpdateSelectedBubbleAfterCollapse( + Consumer<String> updateSelectedBubbleAfterCollapse) { + mUpdateSelectedBubbleAfterCollapse = updateSelectedBubbleAfterCollapse; + } + /** * Sets which bubble view should be shown as selected. */ @@ -217,6 +374,14 @@ public class BubbleBarView extends FrameLayout { } /** + * Sets the dragged bubble view to correctly apply Z order. Dragged view should appear on top + */ + public void setDraggedBubble(@Nullable BubbleView view) { + mDraggedBubbleView = view; + requestLayout(); + } + + /** * Update the arrow position to match the selected bubble. * * @param shouldAnimate whether or not to animate the arrow. If the bar was just expanded, this @@ -247,6 +412,16 @@ public class BubbleBarView extends FrameLayout { } } + private float arrowPositionForSelectedWhenExpanded() { + final int index = indexOfChild(mSelectedBubbleView); + return getPaddingStart() + index * (mIconSize + mIconSpacing) + mIconSize / 2f; + } + + private float arrowPositionForSelectedWhenCollapsed() { + final int index = indexOfChild(mSelectedBubbleView); + return getPaddingStart() + index * (mIconOverlapAmount) + mIconSize / 2f; + } + @Override public void setOnClickListener(View.OnClickListener listener) { mOnClickListener = listener; @@ -264,18 +439,16 @@ public class BubbleBarView extends FrameLayout { /** * Sets whether the bubble bar is expanded or collapsed. */ - // TODO: (b/273592694) animate it public void setExpanded(boolean isBarExpanded) { if (mIsBarExpanded != isBarExpanded) { mIsBarExpanded = isBarExpanded; updateArrowForSelected(/* shouldAnimate= */ false); setOrUnsetClickListener(); - if (!isBarExpanded && mReorderRunnable != null) { - mReorderRunnable.run(); - mReorderRunnable = null; + if (isBarExpanded) { + mWidthAnimator.start(); + } else { + mWidthAnimator.reverse(); } - mBubbleBarBackground.showArrow(mIsBarExpanded); - requestLayout(); // trigger layout to reposition views & update size for expansion } } @@ -286,19 +459,20 @@ public class BubbleBarView extends FrameLayout { return mIsBarExpanded; } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + private float expandedWidth() { final int childCount = getChildCount(); - final float iconWidth = mIsBarExpanded - ? (childCount * (mIconSize + mIconSpacing)) - : mIconSize + ((childCount - 1) * mIconOverlapAmount); - final int totalWidth = (int) iconWidth + getPaddingStart() + getPaddingEnd(); - setMeasuredDimension(totalWidth, MeasureSpec.getSize(heightMeasureSpec)); - - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - measureChild(child, (int) mIconSize, (int) mIconSize); - } + final int horizontalPadding = getPaddingStart() + getPaddingEnd(); + return childCount * (mIconSize + mIconSpacing) + horizontalPadding; + } + + private float collapsedWidth() { + final int childCount = getChildCount(); + final int horizontalPadding = getPaddingStart() + getPaddingEnd(); + // If there are more than 2 bubbles, the first 2 should be visible when collapsed. + // Otherwise just the first bubble should be visible because we don't show the overflow. + return childCount > 2 + ? mIconSize + mIconOverlapAmount + horizontalPadding + : mIconSize + horizontalPadding; } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index 82494c6fda..20b8e3b708 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -24,16 +24,21 @@ import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout; +import androidx.annotation.NonNull; + import com.android.launcher3.R; import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.launcher3.taskbar.TaskbarControllers; +import com.android.launcher3.taskbar.TaskbarInsetsController; +import com.android.launcher3.taskbar.TaskbarStashController; import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.MultiValueAlpha; import com.android.quickstep.SystemUiProxy; import java.util.List; import java.util.Objects; +import java.util.function.Consumer; /** * Controller for {@link BubbleBarView}. Manages the visibility of the bubble bar as well as @@ -51,6 +56,9 @@ public class BubbleBarViewController { // Initialized in init. private BubbleStashController mBubbleStashController; private BubbleBarController mBubbleBarController; + private BubbleDragController mBubbleDragController; + private TaskbarStashController mTaskbarStashController; + private TaskbarInsetsController mTaskbarInsetsController; private View.OnClickListener mBubbleClickListener; private View.OnClickListener mBubbleBarClickListener; @@ -80,6 +88,9 @@ public class BubbleBarViewController { public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) { mBubbleStashController = bubbleControllers.bubbleStashController; mBubbleBarController = bubbleControllers.bubbleBarController; + mBubbleDragController = bubbleControllers.bubbleDragController; + mTaskbarStashController = controllers.taskbarStashController; + mTaskbarInsetsController = controllers.taskbarInsetsController; mActivity.addOnDeviceProfileChangeListener(dp -> mBarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarHeight @@ -88,12 +99,15 @@ public class BubbleBarViewController { mBubbleBarScale.updateValue(1f); mBubbleClickListener = v -> onBubbleClicked(v); mBubbleBarClickListener = v -> setExpanded(true); + mBubbleDragController.setupBubbleBarView(mBarView); mBarView.setOnClickListener(mBubbleBarClickListener); - // TODO: when barView layout changes tell taskbarInsetsController the insets have changed. + mBarView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) -> + mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged() + ); } private void onBubbleClicked(View v) { - BubbleBarBubble bubble = ((BubbleView) v).getBubble(); + BubbleBarItem bubble = ((BubbleView) v).getBubble(); if (bubble == null) { Log.e(TAG, "bubble click listener, bubble was null"); } @@ -103,9 +117,7 @@ public class BubbleBarViewController { setExpanded(false); mBubbleStashController.stashBubbleBar(); } else { - mBubbleBarController.setSelectedBubble(bubble); - mSystemUiProxy.showBubble(bubble.getKey(), - mBubbleStashController.isBubblesShowingOnHome()); + mBubbleBarController.showAndSelectBubble(bubble); } } @@ -133,6 +145,11 @@ public class BubbleBarViewController { return mBarView.getVisibility() == VISIBLE; } + /** Whether the bubble bar has bubbles. */ + public boolean hasBubbles() { + return mBubbleBarController.getSelectedBubbleKey() != null; + } + /** * The bounds of the bubble bar. */ @@ -140,6 +157,11 @@ public class BubbleBarViewController { return mBarView.getBubbleBarBounds(); } + /** The horizontal margin of the bubble bar from the edge of the screen. */ + public int getHorizontalMargin() { + return mBarView.getHorizontalMargin(); + } + /** * When the bubble bar is not stashed, it can be collapsed (the icons are in a stack) or * expanded (the icons are in a row). This indicates whether the bubble bar is expanded. @@ -176,6 +198,12 @@ public class BubbleBarViewController { } } + /** Sets a callback that updates the selected bubble after the bubble bar collapses. */ + public void setUpdateSelectedBubbleAfterCollapse( + Consumer<String> updateSelectedBubbleAfterCollapse) { + mBarView.setUpdateSelectedBubbleAfterCollapse(updateSelectedBubbleAfterCollapse); + } + /** * Sets whether the bubble bar should be hidden due to SysUI state (e.g. on lockscreen). */ @@ -188,10 +216,12 @@ public class BubbleBarViewController { // TODO: (b/273592694) animate it private void updateVisibilityForStateChange() { - if (!mHiddenForSysui && !mBubbleStashController.isStashed() && !mHiddenForNoBubbles) { + if (!mHiddenForSysui && !mHiddenForNoBubbles) { mBarView.setVisibility(VISIBLE); } else { mBarView.setVisibility(INVISIBLE); + mBarView.setAlpha(0); + mBarView.setExpanded(false); } } @@ -228,7 +258,7 @@ public class BubbleBarViewController { /** * Removes the provided bubble from the bubble bar. */ - public void removeBubble(BubbleBarBubble b) { + public void removeBubble(BubbleBarItem b) { if (b != null) { mBarView.removeView(b.getView()); } else { @@ -239,10 +269,11 @@ public class BubbleBarViewController { /** * Adds the provided bubble to the bubble bar. */ - public void addBubble(BubbleBarBubble b) { + public void addBubble(BubbleBarItem b) { if (b != null) { mBarView.addView(b.getView(), 0, new FrameLayout.LayoutParams(mIconSize, mIconSize)); b.getView().setOnClickListener(mBubbleClickListener); + mBubbleDragController.setupBubbleView(b.getView()); } else { Log.w(TAG, "addBubble, bubble was null!"); } @@ -260,7 +291,7 @@ public class BubbleBarViewController { /** * Updates the selected bubble. */ - public void updateSelectedBubble(BubbleBarBubble newlySelected) { + public void updateSelectedBubble(BubbleBarItem newlySelected) { mBarView.setSelectedBubble(newlySelected.getView()); } @@ -276,14 +307,9 @@ public class BubbleBarViewController { if (!isExpanded) { mSystemUiProxy.collapseBubbles(); } else { - final String selectedKey = mBubbleBarController.getSelectedBubbleKey(); - if (selectedKey != null) { - mSystemUiProxy.showBubble(selectedKey, - mBubbleStashController.isBubblesShowingOnHome()); - } else { - Log.w(TAG, "trying to expand bubbles when there isn't one selected"); - } - // TODO: Tell taskbar stash controller to stash without bubbles following + mBubbleBarController.showSelectedBubble(); + mTaskbarStashController.updateAndAnimateTransientTaskbar(true /* stash */, + false /* shouldBubblesFollow */); } } } @@ -299,4 +325,46 @@ public class BubbleBarViewController { mBubbleStashController.showBubbleBar(true /* expand the bubbles */); } } + + /** + * Updates the dragged bubble view in the bubble bar view, and notifies SystemUI + * that a bubble is being dragged to dismiss. + * @param bubbleView dragged bubble view + */ + public void onDragStart(@NonNull BubbleView bubbleView) { + if (bubbleView.getBubble() == null) return; + mSystemUiProxy.onBubbleDrag(bubbleView.getBubble().getKey(), /* isBeingDragged = */ true); + mBarView.setDraggedBubble(bubbleView); + } + + /** + * Notifies SystemUI to expand the selected bubble when the bubble is released. + * @param bubbleView dragged bubble view + */ + public void onDragRelease(@NonNull BubbleView bubbleView) { + if (bubbleView.getBubble() == null) return; + mSystemUiProxy.onBubbleDrag(bubbleView.getBubble().getKey(), /* isBeingDragged = */ false); + } + + /** + * Removes the dragged bubble view in the bubble bar view + */ + public void onDragEnd() { + mBarView.setDraggedBubble(null); + } + + /** + * Called when bubble was dragged into the dismiss target. Notifies System + * @param bubble dismissed bubble item + */ + public void onDismissBubbleWhileDragging(@NonNull BubbleBarItem bubble) { + mSystemUiProxy.removeBubble(bubble.getKey()); + } + + /** + * Called when bubble stack was dragged into the dismiss target + */ + public void onDismissAllBubblesWhileDragging() { + mSystemUiProxy.removeAllBubbles(); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java index 6417f3c585..c47427d4fb 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java @@ -27,6 +27,8 @@ public class BubbleControllers { public final BubbleBarViewController bubbleBarViewController; public final BubbleStashController bubbleStashController; public final BubbleStashedHandleViewController bubbleStashedHandleViewController; + public final BubbleDragController bubbleDragController; + public final BubbleDismissController bubbleDismissController; private final RunnableList mPostInitRunnables = new RunnableList(); @@ -39,11 +41,15 @@ public class BubbleControllers { BubbleBarController bubbleBarController, BubbleBarViewController bubbleBarViewController, BubbleStashController bubbleStashController, - BubbleStashedHandleViewController bubbleStashedHandleViewController) { + BubbleStashedHandleViewController bubbleStashedHandleViewController, + BubbleDragController bubbleDragController, + BubbleDismissController bubbleDismissController) { this.bubbleBarController = bubbleBarController; this.bubbleBarViewController = bubbleBarViewController; this.bubbleStashController = bubbleStashController; this.bubbleStashedHandleViewController = bubbleStashedHandleViewController; + this.bubbleDragController = bubbleDragController; + this.bubbleDismissController = bubbleDismissController; } /** @@ -56,6 +62,8 @@ public class BubbleControllers { bubbleBarViewController.init(taskbarControllers, this); bubbleStashedHandleViewController.init(taskbarControllers, this); bubbleStashController.init(taskbarControllers, this); + bubbleDragController.init(/* bubbleControllers = */ this); + bubbleDismissController.init(/* bubbleControllers = */ this); mPostInitRunnables.executeAllAndDestroy(); } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java new file mode 100644 index 0000000000..41c3dec5d5 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java @@ -0,0 +1,206 @@ +/* + * 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.launcher3.taskbar.bubbles; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.dynamicanimation.animation.DynamicAnimation; + +import com.android.launcher3.R; +import com.android.launcher3.taskbar.TaskbarActivityContext; +import com.android.launcher3.taskbar.TaskbarDragLayer; +import com.android.wm.shell.common.bubbles.DismissView; +import com.android.wm.shell.common.magnetictarget.MagnetizedObject; + +/** + * Controls dismiss view presentation for the bubble bar dismiss functionality. + * Provides the dragged view snapping to the target dismiss area and animates it. + * When the dragged bubble/bubble stack is released inside of the target area, it gets dismissed. + * + * @see BubbleDragController + */ +public class BubbleDismissController { + private static final String TAG = BubbleDismissController.class.getSimpleName(); + private static final float FLING_TO_DISMISS_MIN_VELOCITY = 6000f; + private final TaskbarActivityContext mActivity; + private final TaskbarDragLayer mDragLayer; + @Nullable + private BubbleBarViewController mBubbleBarViewController; + + // Dismiss view that's attached to drag layer. It consists of the scrim view and the circular + // dismiss view used as a dismiss target. + @Nullable + private DismissView mDismissView; + + // The currently magnetized object, which is being dragged and will be attracted to the magnetic + // dismiss target. This is either the stack itself, or an individual bubble. + @Nullable + private MagnetizedObject<View> mMagnetizedObject; + + // The MagneticTarget instance for our circular dismiss view. This is added to the + // MagnetizedObject instances for the stack and any dragged-out bubbles. + @Nullable + private MagnetizedObject.MagneticTarget mMagneticTarget; + + // The bubble drag animator that synchronizes bubble drag and dismiss view animations + // A new instance is provided when the dismiss view is setup + @Nullable + private BubbleDragAnimator mAnimator; + + public BubbleDismissController(TaskbarActivityContext activity, TaskbarDragLayer dragLayer) { + mActivity = activity; + mDragLayer = dragLayer; + } + + /** + * Initializes dependencies when bubble controllers are created. + * Should be careful to only access things that were created in constructors for now, as some + * controllers may still be waiting for init(). + */ + public void init(@NonNull BubbleControllers bubbleControllers) { + mBubbleBarViewController = bubbleControllers.bubbleBarViewController; + } + + /** + * Setup the dismiss view and magnetized object that will be attracted to magnetic target. + * Should be called before handling events or showing/hiding dismiss view. + * + * @param magnetizedView the view to be pulled into target dismiss area + * @param animator the bubble animator to be used for the magnetized view, it syncs bubble + * dragging and dismiss animations with the dismiss view provided. + */ + public void setupDismissView(@NonNull View magnetizedView, + @NonNull BubbleDragAnimator animator) { + setupDismissView(); + setupMagnetizedObject(magnetizedView); + if (mDismissView != null) { + animator.setDismissView(mDismissView); + mAnimator = animator; + } + } + + /** + * Handle the touch event and pass it to the magnetized object. + * It should be called after {@code setupDismissView} + */ + public boolean handleTouchEvent(@NonNull MotionEvent event) { + return mMagnetizedObject != null && mMagnetizedObject.maybeConsumeMotionEvent(event); + } + + /** + * Show dismiss view with animation + * It should be called after {@code setupDismissView} + */ + public void showDismissView() { + if (mDismissView == null) return; + mDismissView.show(); + } + + /** + * Hide dismiss view with animation + * It should be called after {@code setupDismissView} + */ + public void hideDismissView() { + if (mDismissView == null) return; + mDismissView.hide(); + } + + /** + * Dismiss magnetized object when it's released in the dismiss target area + */ + private void dismissMagnetizedObject() { + if (mMagnetizedObject == null || mBubbleBarViewController == null) return; + if (mMagnetizedObject.getUnderlyingObject() instanceof BubbleView) { + BubbleView bubbleView = (BubbleView) mMagnetizedObject.getUnderlyingObject(); + if (bubbleView.getBubble() != null) { + mBubbleBarViewController.onDismissBubbleWhileDragging(bubbleView.getBubble()); + } + } else if (mMagnetizedObject.getUnderlyingObject() instanceof BubbleBarView) { + mBubbleBarViewController.onDismissAllBubblesWhileDragging(); + } + } + + private void setupDismissView() { + if (mDismissView != null) return; + mDismissView = new DismissView(mActivity.getApplicationContext()); + BubbleDismissViewUtils.setup(mDismissView); + mDragLayer.addView(mDismissView, /* index = */ 0, + new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); + mDismissView.setElevation(mDismissView.getResources().getDimensionPixelSize( + R.dimen.bubblebar_elevation)); + setupMagneticTarget(mDismissView.getCircle()); + } + + private void setupMagneticTarget(@NonNull View view) { + int magneticFieldRadius = mActivity.getResources().getDimensionPixelSize( + R.dimen.bubblebar_dismiss_target_size); + mMagneticTarget = new MagnetizedObject.MagneticTarget(view, magneticFieldRadius); + } + + private void setupMagnetizedObject(@NonNull View magnetizedView) { + mMagnetizedObject = new MagnetizedObject<>(mActivity.getApplicationContext(), + magnetizedView, DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y) { + @Override + public float getWidth(@NonNull View underlyingObject) { + return underlyingObject.getWidth() * underlyingObject.getScaleX(); + } + + @Override + public float getHeight(@NonNull View underlyingObject) { + return underlyingObject.getHeight() * underlyingObject.getScaleY(); + } + + @Override + public void getLocationOnScreen(@NonNull View underlyingObject, @NonNull int[] loc) { + underlyingObject.getLocationOnScreen(loc); + } + }; + + mMagnetizedObject.setHapticsEnabled(true); + mMagnetizedObject.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY); + if (mMagneticTarget != null) { + mMagnetizedObject.addTarget(mMagneticTarget); + } else { + Log.e(TAG,"Requires MagneticTarget to add target to MagnetizedObject!"); + } + mMagnetizedObject.setMagnetListener(new MagnetizedObject.MagnetListener() { + @Override + public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) { + if (mAnimator == null) return; + mAnimator.animateDismissCaptured(); + } + + @Override + public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target, + float velX, float velY, boolean wasFlungOut) { + if (mAnimator == null) return; + mAnimator.animateDismissReleased(); + } + + @Override + public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) { + dismissMagnetizedObject(); + } + }); + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt new file mode 100644 index 0000000000..4b235a97d2 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt @@ -0,0 +1,42 @@ +/* + * 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. + */ +@file:JvmName("BubbleDismissViewUtils") + +package com.android.launcher3.taskbar.bubbles + +import com.android.launcher3.R +import com.android.wm.shell.common.bubbles.DismissView + +/** + * Dismiss view is shared from WMShell. It requires setup with local resources. + * + * Usage: + * - Kotlin `dismissView.setup()` + * - Java `BubbleDismissViewUtils.setup(dismissView)` + */ +fun DismissView.setup() { + setup( + DismissView.Config( + targetSizeResId = R.dimen.bubblebar_dismiss_target_size, + iconSizeResId = R.dimen.bubblebar_dismiss_target_icon_size, + bottomMarginResId = R.dimen.bubblebar_dismiss_target_bottom_margin, + floatingGradientHeightResId = R.dimen.bubblebar_dismiss_floating_gradient_height, + floatingGradientColorResId = android.R.color.system_neutral1_900, + backgroundResId = R.drawable.bg_bubble_dismiss_circle, + iconResId = R.drawable.ic_bubble_dismiss_white + ) + ) +} diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java new file mode 100644 index 0000000000..24dca5e989 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java @@ -0,0 +1,222 @@ +/* + * 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.launcher3.taskbar.bubbles; + +import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY; +import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW; +import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM; + +import android.content.res.Resources; +import android.graphics.PointF; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.dynamicanimation.animation.DynamicAnimation; +import androidx.dynamicanimation.animation.FloatPropertyCompat; + +import com.android.launcher3.R; +import com.android.wm.shell.animation.PhysicsAnimator; +import com.android.wm.shell.common.bubbles.DismissCircleView; +import com.android.wm.shell.common.bubbles.DismissView; + +/** + * The animator performs the bubble animations while dragging and coordinates bubble and dismiss + * view animations when it gets magnetized, released or dismissed. + */ +public class BubbleDragAnimator { + private static final float SCALE_BUBBLE_FOCUSED = 1.2f; + private static final float SCALE_BUBBLE_CAPTURED = 0.9f; + private static final float SCALE_BUBBLE_BAR_FOCUSED = 1.1f; + + private final PhysicsAnimator.SpringConfig mDefaultConfig = + new PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY); + private final PhysicsAnimator.SpringConfig mTranslationConfig = + new PhysicsAnimator.SpringConfig(STIFFNESS_MEDIUM, DAMPING_RATIO_LOW_BOUNCY); + @NonNull + private final View mView; + @NonNull + private final PhysicsAnimator<View> mBubbleAnimator; + @Nullable + private DismissView mDismissView; + @Nullable + private PhysicsAnimator<DismissCircleView> mDismissAnimator; + private final float mBubbleFocusedScale; + private final float mBubbleCapturedScale; + private final float mDismissCapturedScale; + + /** + * Should be initialised for each dragged view + * + * @param view the dragged view to animate + */ + public BubbleDragAnimator(@NonNull View view) { + mView = view; + mBubbleAnimator = PhysicsAnimator.getInstance(view); + mBubbleAnimator.setDefaultSpringConfig(mDefaultConfig); + + Resources resources = view.getResources(); + final int collapsedSize = resources.getDimensionPixelSize( + R.dimen.bubblebar_dismiss_target_small_size); + final int expandedSize = resources.getDimensionPixelSize( + R.dimen.bubblebar_dismiss_target_size); + mDismissCapturedScale = (float) collapsedSize / expandedSize; + + if (view instanceof BubbleBarView) { + mBubbleFocusedScale = SCALE_BUBBLE_BAR_FOCUSED; + mBubbleCapturedScale = mDismissCapturedScale; + } else { + mBubbleFocusedScale = SCALE_BUBBLE_FOCUSED; + mBubbleCapturedScale = SCALE_BUBBLE_CAPTURED; + } + } + + /** + * Sets dismiss view to be animated alongside the dragged bubble + */ + public void setDismissView(@NonNull DismissView dismissView) { + mDismissView = dismissView; + mDismissAnimator = PhysicsAnimator.getInstance(dismissView.getCircle()); + mDismissAnimator.setDefaultSpringConfig(mDefaultConfig); + } + + /** + * Animates the focused state of the bubble when the dragging starts + */ + public void animateFocused() { + mBubbleAnimator.cancel(); + mBubbleAnimator + .spring(DynamicAnimation.SCALE_X, mBubbleFocusedScale) + .spring(DynamicAnimation.SCALE_Y, mBubbleFocusedScale) + .start(); + } + + /** + * Animates the dragged bubble movement back to the initial position. + * + * @param initialPosition the position to animate to + * @param velocity the initial velocity to use for the spring animation + * @param endActions gets called when the animation completes or gets cancelled + */ + public void animateToInitialState(@NonNull PointF initialPosition, @NonNull PointF velocity, + @Nullable Runnable endActions) { + mBubbleAnimator.cancel(); + mBubbleAnimator + .spring(DynamicAnimation.SCALE_X, 1f) + .spring(DynamicAnimation.SCALE_Y, 1f) + .spring(DynamicAnimation.TRANSLATION_X, initialPosition.x, velocity.x, + mTranslationConfig) + .spring(DynamicAnimation.TRANSLATION_Y, initialPosition.y, velocity.y, + mTranslationConfig) + .addEndListener((View target, @NonNull FloatPropertyCompat<? super View> property, + boolean wasFling, boolean canceled, float finalValue, float finalVelocity, + boolean allRelevantPropertyAnimationsEnded) -> { + if (canceled || allRelevantPropertyAnimationsEnded) { + resetAnimatedViews(initialPosition); + if (endActions != null) { + endActions.run(); + } + } + }) + .start(); + } + + /** + * Animates the dragged view alongside the dismiss view when it gets captured in the dismiss + * target area. + */ + public void animateDismissCaptured() { + mBubbleAnimator.cancel(); + mBubbleAnimator + .spring(DynamicAnimation.SCALE_X, mBubbleCapturedScale) + .spring(DynamicAnimation.SCALE_Y, mBubbleCapturedScale) + .spring(DynamicAnimation.ALPHA, mDismissCapturedScale) + .start(); + + if (mDismissAnimator != null) { + mDismissAnimator.cancel(); + mDismissAnimator + .spring(DynamicAnimation.SCALE_X, mDismissCapturedScale) + .spring(DynamicAnimation.SCALE_Y, mDismissCapturedScale) + .start(); + } + } + + /** + * Animates the dragged view alongside the dismiss view when it gets released from the dismiss + * target area. + */ + public void animateDismissReleased() { + mBubbleAnimator.cancel(); + mBubbleAnimator + .spring(DynamicAnimation.SCALE_X, mBubbleFocusedScale) + .spring(DynamicAnimation.SCALE_Y, mBubbleFocusedScale) + .spring(DynamicAnimation.ALPHA, 1f) + .start(); + + if (mDismissAnimator != null) { + mDismissAnimator.cancel(); + mDismissAnimator + .spring(DynamicAnimation.SCALE_X, 1f) + .spring(DynamicAnimation.SCALE_Y, 1f) + .start(); + } + } + + /** + * Animates the dragged bubble dismiss when it's released in the dismiss target area. + * + * @param initialPosition the initial position to move the bubble too after animation finishes + * @param endActions gets called when the animation completes or gets cancelled + */ + public void animateDismiss(@NonNull PointF initialPosition, @Nullable Runnable endActions) { + float dismissHeight = mDismissView != null ? mDismissView.getHeight() : 0f; + float translationY = mView.getTranslationY() + dismissHeight; + mBubbleAnimator + .spring(DynamicAnimation.TRANSLATION_Y, translationY) + .spring(DynamicAnimation.SCALE_X, 0f) + .spring(DynamicAnimation.SCALE_Y, 0f) + .spring(DynamicAnimation.ALPHA, 0f) + .addEndListener((View target, @NonNull FloatPropertyCompat<? super View> property, + boolean wasFling, boolean canceled, float finalValue, float finalVelocity, + boolean allRelevantPropertyAnimationsEnded) -> { + if (canceled || allRelevantPropertyAnimationsEnded) { + resetAnimatedViews(initialPosition); + if (endActions != null) endActions.run(); + } + }) + .start(); + } + + /** + * Reset the animated views to the initial state + * + * @param initialPosition position of the bubble + */ + private void resetAnimatedViews(@NonNull PointF initialPosition) { + mView.setScaleX(1f); + mView.setScaleY(1f); + mView.setAlpha(1f); + mView.setTranslationX(initialPosition.x); + mView.setTranslationY(initialPosition.y); + + if (mDismissView != null) { + mDismissView.getCircle().setScaleX(1f); + mDismissView.getCircle().setScaleY(1f); + } + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java new file mode 100644 index 0000000000..08fd681b4c --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java @@ -0,0 +1,355 @@ +/* + * 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.launcher3.taskbar.bubbles; + +import android.annotation.SuppressLint; +import android.graphics.PointF; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.taskbar.TaskbarActivityContext; + +/** + * Controls bubble bar drag to dismiss interaction. + * Interacts with {@link BubbleDismissController}, used by {@link BubbleBarViewController}. + * Supported interactions: + * - Drag a single bubble view into dismiss target to remove it. + * - Drag the bubble stack into dismiss target to remove all. + * Restores initial position of dragged view if released outside of the dismiss target. + */ +public class BubbleDragController { + private final TaskbarActivityContext mActivity; + private BubbleBarViewController mBubbleBarViewController; + private BubbleDismissController mBubbleDismissController; + + public BubbleDragController(TaskbarActivityContext activity) { + mActivity = activity; + } + + /** + * Initializes dependencies when bubble controllers are created. + * Should be careful to only access things that were created in constructors for now, as some + * controllers may still be waiting for init(). + */ + public void init(@NonNull BubbleControllers bubbleControllers) { + mBubbleBarViewController = bubbleControllers.bubbleBarViewController; + mBubbleDismissController = bubbleControllers.bubbleDismissController; + } + + /** + * Setup the bubble view for dragging and attach touch listener to it + */ + @SuppressLint("ClickableViewAccessibility") + public void setupBubbleView(@NonNull BubbleView bubbleView) { + if (!(bubbleView.getBubble() instanceof BubbleBarBubble)) { + // Don't setup dragging for overflow bubble view + return; + } + + bubbleView.setOnTouchListener(new BubbleTouchListener() { + @Override + void onDragStart() { + mBubbleBarViewController.onDragStart(bubbleView); + } + + @Override + void onDragEnd() { + mBubbleBarViewController.onDragEnd(); + } + + @Override + protected void onDragRelease() { + mBubbleBarViewController.onDragRelease(bubbleView); + } + }); + } + + /** + * Setup the bubble bar view for dragging and attach touch listener to it + */ + @SuppressLint("ClickableViewAccessibility") + public void setupBubbleBarView(@NonNull BubbleBarView bubbleBarView) { + PointF initialRelativePivot = new PointF(); + bubbleBarView.setOnTouchListener(new BubbleTouchListener() { + @Override + protected boolean onTouchDown(@NonNull View view, @NonNull MotionEvent event) { + if (bubbleBarView.isExpanded()) return false; + return super.onTouchDown(view, event); + } + + @Override + void onDragStart() { + initialRelativePivot.set(bubbleBarView.getRelativePivotX(), + bubbleBarView.getRelativePivotY()); + // By default the bubble bar view pivot is in bottom right corner, while dragging + // it should be centered in order to align it with the dismiss target view + bubbleBarView.setRelativePivot(/* x = */ 0.5f, /* y = */ 0.5f); + } + + @Override + void onDragEnd() { + // Restoring the initial pivot for the bubble bar view + bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y); + } + }); + } + + /** + * Bubble touch listener for handling a single bubble view or bubble bar view while dragging. + * The dragging starts after "shorter" long click (the long click duration might change): + * - When the touch gesture moves out of the {@code ACTION_DOWN} location the dragging + * interaction is cancelled. + * - When {@code ACTION_UP} happens before long click is registered and there was no significant + * movement the view will perform click. + * - When the listener registers long click it starts dragging interaction, all the subsequent + * {@code ACTION_MOVE} events will drag the view, and the interaction finishes when + * {@code ACTION_UP} or {@code ACTION_CANCEL} are received. + * Lifecycle methods can be overridden do add extra setup/clean up steps. + */ + private abstract class BubbleTouchListener implements View.OnTouchListener { + /** + * The internal state of the touch listener + */ + private enum State { + // Idle and ready for the touch events. + // Changes to: + // - TOUCHED, when the {@code ACTION_DOWN} is handled + IDLE, + + // Touch down was handled and the lister is recognising the gestures. + // Changes to: + // - IDLE, when performs the click + // - DRAGGING, when registers the long click and starts dragging interaction + // - CANCELLED, when the touch events move out of the initial location before the long + // click is recognised + + TOUCHED, + + // The long click was registered and the view is being dragged. + // Changes to: + // - IDLE, when the gesture ends with the {@code ACTION_UP} or {@code ACTION_CANCEL} + DRAGGING, + + // The dragging was cancelled. + // Changes to: + // - IDLE, when the current gesture completes + CANCELLED + } + + private final PointF mTouchDownLocation = new PointF(); + private final PointF mViewInitialPosition = new PointF(); + private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); + private final long mPressToDragTimeout = ViewConfiguration.getLongPressTimeout() / 2; + private State mState = State.IDLE; + private int mTouchSlop = -1; + private BubbleDragAnimator mAnimator; + @Nullable + private Runnable mLongClickRunnable; + + /** + * Called when the dragging interaction has started + */ + abstract void onDragStart(); + + /** + * Called when the dragging interaction has ended and all the animations have completed + */ + abstract void onDragEnd(); + + /** + * Called when the dragged bubble is released outside of the dismiss target area and will + * move back to its initial position + */ + protected void onDragRelease() { + } + + /** + * Called when the dragged bubble is released inside of the dismiss target area and will get + * dismissed with animation + */ + protected void onDragDismiss() { + } + + @Override + @SuppressLint("ClickableViewAccessibility") + public boolean onTouch(@NonNull View view, @NonNull MotionEvent event) { + updateVelocity(event); + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + return onTouchDown(view, event); + case MotionEvent.ACTION_MOVE: + onTouchMove(view, event); + break; + case MotionEvent.ACTION_UP: + onTouchUp(view, event); + break; + case MotionEvent.ACTION_CANCEL: + onTouchCancel(view, event); + break; + } + return true; + } + + /** + * The touch down starts the interaction and schedules the long click handler. + * + * @param view the view that received the event + * @param event the motion event + * @return true if the gesture should be intercepted and handled, false otherwise. Note if + * the false is returned subsequent events in the gesture won't get reported. + */ + protected boolean onTouchDown(@NonNull View view, @NonNull MotionEvent event) { + mState = State.TOUCHED; + mTouchSlop = ViewConfiguration.get(view.getContext()).getScaledTouchSlop(); + mTouchDownLocation.set(event.getRawX(), event.getRawY()); + mViewInitialPosition.set(view.getTranslationX(), view.getTranslationY()); + setupLongClickHandler(view); + return true; + } + + /** + * The move event drags the view or cancels the interaction if hasn't long clicked yet. + * + * @param view the view that received the event + * @param event the motion event + */ + protected void onTouchMove(@NonNull View view, @NonNull MotionEvent event) { + final float dx = event.getRawX() - mTouchDownLocation.x; + final float dy = event.getRawY() - mTouchDownLocation.y; + switch (mState) { + case TOUCHED: + final boolean movedOut = Math.hypot(dx, dy) > mTouchSlop; + if (movedOut) { + // Moved out of the initial location before the long click was registered + mState = State.CANCELLED; + cleanUpLongClickHandler(view); + } + break; + case DRAGGING: + drag(view, event, dx, dy); + break; + } + } + + /** + * On touch up performs click or finishes the dragging depending on the state. + * + * @param view the view that received the event + * @param event the motion event + */ + protected void onTouchUp(@NonNull View view, @NonNull MotionEvent event) { + switch (mState) { + case TOUCHED: + view.performClick(); + cleanUp(view); + break; + case DRAGGING: + stopDragging(view, event); + break; + default: + cleanUp(view); + break; + } + } + + /** + * The gesture is cancelled and the interaction should clean up and complete. + * + * @param view the view that received the event + * @param event the motion event + */ + protected void onTouchCancel(@NonNull View view, @NonNull MotionEvent event) { + if (mState == State.DRAGGING) { + stopDragging(view, event); + } else { + cleanUp(view); + } + } + + private void startDragging(@NonNull View view) { + onDragStart(); + mActivity.setTaskbarWindowFullscreen(true); + mAnimator = new BubbleDragAnimator(view); + mAnimator.animateFocused(); + mBubbleDismissController.setupDismissView(view, mAnimator); + mBubbleDismissController.showDismissView(); + } + + private void drag(@NonNull View view, @NonNull MotionEvent event, float dx, float dy) { + if (mBubbleDismissController.handleTouchEvent(event)) return; + view.setTranslationX(mViewInitialPosition.x + dx); + view.setTranslationY(mViewInitialPosition.y + dy); + } + + private void stopDragging(@NonNull View view, @NonNull MotionEvent event) { + Runnable onComplete = () -> { + mActivity.setTaskbarWindowFullscreen(false); + cleanUp(view); + onDragEnd(); + }; + + if (mBubbleDismissController.handleTouchEvent(event)) { + onDragDismiss(); + mAnimator.animateDismiss(mViewInitialPosition, onComplete); + } else { + onDragRelease(); + mAnimator.animateToInitialState(mViewInitialPosition, getCurrentVelocity(), + onComplete); + } + mBubbleDismissController.hideDismissView(); + } + + private void setupLongClickHandler(@NonNull View view) { + cleanUpLongClickHandler(view); + mLongClickRunnable = () -> { + // Register long click and start dragging interaction + mState = State.DRAGGING; + startDragging(view); + }; + view.getHandler().postDelayed(mLongClickRunnable, mPressToDragTimeout); + } + + private void cleanUpLongClickHandler(@NonNull View view) { + if (mLongClickRunnable == null || view.getHandler() == null) return; + view.getHandler().removeCallbacks(mLongClickRunnable); + mLongClickRunnable = null; + } + + private void cleanUp(@NonNull View view) { + cleanUpLongClickHandler(view); + mVelocityTracker.clear(); + mState = State.IDLE; + } + + private void updateVelocity(MotionEvent event) { + final float deltaX = event.getRawX() - event.getX(); + final float deltaY = event.getRawY() - event.getY(); + event.offsetLocation(deltaX, deltaY); + mVelocityTracker.addMovement(event); + event.offsetLocation(-deltaX, -deltaY); + } + + private PointF getCurrentVelocity() { + mVelocityTracker.computeCurrentVelocity(/* units = */ 1000); + return new PointF(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); + } + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java index 0ab53b0c20..a5ea5a9955 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.taskbar.bubbles; +import static java.lang.Math.abs; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -25,6 +27,7 @@ import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.taskbar.StashedHandleViewController; import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.launcher3.taskbar.TaskbarControllers; +import com.android.launcher3.taskbar.TaskbarInsetsController; import com.android.launcher3.taskbar.TaskbarStashController; import com.android.launcher3.util.MultiPropertyFactory; @@ -50,6 +53,7 @@ public class BubbleStashController { // Initialized in init. private TaskbarControllers mControllers; + private TaskbarInsetsController mTaskbarInsetsController; private BubbleBarViewController mBarViewController; private BubbleStashedHandleViewController mHandleViewController; private TaskbarStashController mTaskbarStashController; @@ -67,6 +71,7 @@ public class BubbleStashController { private int mUnstashedHeight; private boolean mBubblesShowingOnHome; private boolean mBubblesShowingOnOverview; + private boolean mIsSysuiLocked; @Nullable private AnimatorSet mAnimator; @@ -77,6 +82,7 @@ public class BubbleStashController { public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) { mControllers = controllers; + mTaskbarInsetsController = controllers.taskbarInsetsController; mBarViewController = bubbleControllers.bubbleBarViewController; mHandleViewController = bubbleControllers.bubbleStashedHandleViewController; mTaskbarStashController = controllers.taskbarStashController; @@ -90,14 +96,6 @@ public class BubbleStashController { mStashedHeight = mHandleViewController.getStashedHeight(); mUnstashedHeight = mHandleViewController.getUnstashedHeight(); - - bubbleControllers.runAfterInit(() -> { - if (mTaskbarStashController.isStashed()) { - stashBubbleBar(); - } else { - showBubbleBar(false /* expandBubbles */); - } - }); } /** @@ -115,13 +113,63 @@ public class BubbleStashController { } /** + * Animates the bubble bar and handle to their initial state, transitioning from the state where + * both views are invisible. Called when the first bubble is added or when the device is + * unlocked. + * + * <p>Normally either the bubble bar or the handle is visible, + * and {@link #showBubbleBar(boolean)} and {@link #stashBubbleBar()} are used to transition + * between these two states. But the transition from the state where both the bar and handle + * are invisible is slightly different. + * + * <p>The initial state will depend on the current state of the device, i.e. overview, home etc + * and whether bubbles are requested to be expanded. + */ + public void animateToInitialState(boolean expanding) { + AnimatorSet animatorSet = new AnimatorSet(); + if (expanding || mBubblesShowingOnHome || mBubblesShowingOnOverview) { + mIsStashed = false; + animatorSet.playTogether(mIconScaleForStash.animateToValue(1), + mIconTranslationYForStash.animateToValue(getBubbleBarTranslationY()), + mIconAlphaForStash.animateToValue(1)); + } else { + mIsStashed = true; + animatorSet.playTogether(mBubbleStashedHandleAlpha.animateToValue(1)); + } + + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + onIsStashedChanged(); + } + }); + animatorSet.setDuration(BAR_STASH_DURATION).start(); + } + + /** * Called when launcher enters or exits the home page. Bubbles are unstashed on home. */ public void setBubblesShowingOnHome(boolean onHome) { if (mBubblesShowingOnHome != onHome) { mBubblesShowingOnHome = onHome; + + if (!mBarViewController.hasBubbles()) { + // if there are no bubbles, there's nothing to show, so just return. + return; + } + if (mBubblesShowingOnHome) { showBubbleBar(/* expanded= */ false); + // When transitioning from app to home the stash animator may already have been + // created, so we need to animate the bubble bar here to align with hotseat. + if (!mIsStashed) { + mIconTranslationYForStash.animateToValue(getBubbleBarTranslationYForHotseat()) + .start(); + } + // If the bubble bar is already unstashed, the taskbar touchable region won't be + // updated correctly, so force an update here. + mControllers.runAfterInit(() -> + mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()); } else if (!mBarViewController.isExpanded()) { stashBubbleBar(); } @@ -140,18 +188,27 @@ public class BubbleStashController { mBubblesShowingOnOverview = onOverview; if (!mBubblesShowingOnOverview && !mBarViewController.isExpanded()) { stashBubbleBar(); + } else { + // When transitioning to overview the stash animator may already have been + // created, so we need to animate the bubble bar here to align with taskbar. + mIconTranslationYForStash.animateToValue(getBubbleBarTranslationYForTaskbar()) + .start(); } } } + /** Whether bubbles are showing on Overview. */ + public boolean isBubblesShowingOnOverview() { + return mBubblesShowingOnOverview; + } + /** Called when sysui locked state changes, when locked, bubble bar is stashed. */ public void onSysuiLockedStateChange(boolean isSysuiLocked) { - if (isSysuiLocked) { - // TODO: should the normal path flip mBubblesOnHome / check if this is needed - // If we're locked, we're no longer showing on home. - mBubblesShowingOnHome = false; - mBubblesShowingOnOverview = false; - stashBubbleBar(); + if (isSysuiLocked != mIsSysuiLocked) { + mIsSysuiLocked = isSysuiLocked; + if (!mIsSysuiLocked && mBarViewController.hasBubbles()) { + animateToInitialState(false /* expanding */); + } } } @@ -230,9 +287,8 @@ public class BubbleStashController { firstHalfDurationScale = 0.5f; secondHalfDurationScale = 0.75f; - // If we're on home, adjust the translation so the bubble bar aligns with hotseat. - final float hotseatTransY = mActivity.getDeviceProfile().getTaskbarOffsetY(); - final float translationY = mBubblesShowingOnHome ? hotseatTransY : 0; + final float translationY = getBubbleBarTranslationY(); + fullLengthAnimatorSet.playTogether( mIconScaleForStash.animateToValue(1), mIconTranslationYForStash.animateToValue(translationY)); @@ -262,6 +318,7 @@ public class BubbleStashController { if (isStashed) { mBarViewController.setExpanded(false); } + mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged(); }); } }); @@ -271,7 +328,26 @@ public class BubbleStashController { private void onIsStashedChanged() { mControllers.runAfterInit(() -> { mHandleViewController.onIsStashedChanged(); - // TODO: when stash changes tell taskbarInsetsController the insets have changed. + mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged(); }); } + + private float getBubbleBarTranslationYForTaskbar() { + return -mActivity.getDeviceProfile().taskbarBottomMargin; + } + + private float getBubbleBarTranslationYForHotseat() { + final float hotseatBottomSpace = mActivity.getDeviceProfile().hotseatBarBottomSpacePx; + final float hotseatCellHeight = mActivity.getDeviceProfile().hotseatCellHeightPx; + return -hotseatBottomSpace - hotseatCellHeight + mUnstashedHeight - abs( + hotseatCellHeight - mUnstashedHeight) / 2; + } + + float getBubbleBarTranslationY() { + // If we're on home, adjust the translation so the bubble bar aligns with hotseat. + // Otherwise we're either showing in an app or in overview. In either case adjust it so + // the bubble bar aligns with the taskbar. + return mBubblesShowingOnHome ? getBubbleBarTranslationYForHotseat() + : getBubbleBarTranslationYForTaskbar(); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java index 2170a5dc5b..fbab59531c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java @@ -52,6 +52,7 @@ public class BubbleStashedHandleViewController { private BubbleStashController mBubbleStashController; private RegionSamplingHelper mRegionSamplingHelper; private int mBarSize; + private int mStashedTaskbarHeight; private int mStashedHandleWidth; private int mStashedHandleHeight; @@ -92,7 +93,7 @@ public class BubbleStashedHandleViewController { mTaskbarStashedHandleAlpha.get(0).setValue(0); - final int stashedTaskbarHeight = resources.getDimensionPixelSize( + mStashedTaskbarHeight = resources.getDimensionPixelSize( R.dimen.bubblebar_stashed_size); mStashedHandleView.setOutlineProvider(new ViewOutlineProvider() { @Override @@ -115,22 +116,27 @@ public class BubbleStashedHandleViewController { } }, Executors.UI_HELPER_EXECUTOR); - mStashedHandleView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) -> { - // As more bubbles get added, the icon bounds become larger. To ensure a consistent - // handle bar position, we pin it to the edge of the screen. - Rect bubblebarRect = mBarViewController.getBubbleBarBounds(); - final int stashedCenterY = view.getHeight() - stashedTaskbarHeight / 2; - - mStashedHandleBounds.set( - bubblebarRect.right - mStashedHandleWidth, - stashedCenterY - mStashedHandleHeight / 2, - bubblebarRect.right, - stashedCenterY + mStashedHandleHeight / 2); - mStashedHandleView.updateSampledRegion(mStashedHandleBounds); - - view.setPivotX(view.getWidth()); - view.setPivotY(view.getHeight() - stashedTaskbarHeight / 2f); - }); + mStashedHandleView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) -> + updateBounds()); + } + + private void updateBounds() { + // As more bubbles get added, the icon bounds become larger. To ensure a consistent + // handle bar position, we pin it to the edge of the screen. + final int right = + mActivity.getDeviceProfile().widthPx - mBarViewController.getHorizontalMargin(); + + final int stashedCenterY = mStashedHandleView.getHeight() - mStashedTaskbarHeight / 2; + + mStashedHandleBounds.set( + right - mStashedHandleWidth, + stashedCenterY - mStashedHandleHeight / 2, + right, + stashedCenterY + mStashedHandleHeight / 2); + mStashedHandleView.updateSampledRegion(mStashedHandleBounds); + + mStashedHandleView.setPivotX(mStashedHandleView.getWidth()); + mStashedHandleView.setPivotY(mStashedHandleView.getHeight() - mStashedTaskbarHeight / 2f); } public void onDestroy() { @@ -188,6 +194,7 @@ public class BubbleStashedHandleViewController { mStashedHandleView.setVisibility(VISIBLE); } else { mStashedHandleView.setVisibility(INVISIBLE); + mStashedHandleView.setAlpha(0); } updateRegionSampling(); } @@ -203,12 +210,14 @@ public class BubbleStashedHandleViewController { private void updateRegionSampling() { boolean handleVisible = mStashedHandleView.getVisibility() == VISIBLE && mBubbleStashController.isStashed(); - mRegionSamplingHelper.setWindowVisible(handleVisible); - if (handleVisible) { - mStashedHandleView.updateSampledRegion(mStashedHandleBounds); - mRegionSamplingHelper.start(mStashedHandleView.getSampledRegion()); - } else { - mRegionSamplingHelper.stop(); + if (mRegionSamplingHelper != null) { + mRegionSamplingHelper.setWindowVisible(handleVisible); + if (handleVisible) { + mStashedHandleView.updateSampledRegion(mStashedHandleBounds); + mRegionSamplingHelper.start(mStashedHandleView.getSampledRegion()); + } else { + mRegionSamplingHelper.stop(); + } } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java index e22e63aa36..12cb8c5353 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java @@ -18,7 +18,9 @@ package com.android.launcher3.taskbar.bubbles; import android.annotation.Nullable; import android.content.Context; import android.graphics.Bitmap; +import android.graphics.Canvas; import android.graphics.Outline; +import android.graphics.Rect; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -28,27 +30,59 @@ import android.widget.ImageView; import androidx.constraintlayout.widget.ConstraintLayout; import com.android.launcher3.R; +import com.android.launcher3.icons.DotRenderer; import com.android.launcher3.icons.IconNormalizer; +import com.android.wm.shell.animation.Interpolators; + +import java.util.EnumSet; // TODO: (b/276978250) This is will be similar to WMShell's BadgedImageView, it'd be nice to share. -// TODO: (b/269670235) currently this doesn't show the 'update dot' + /** * View that displays a bubble icon, along with an app badge on either the left or * right side of the view. */ public class BubbleView extends ConstraintLayout { - // TODO: (b/269670235) currently we don't render the 'update dot', this will be used for that. public static final int DEFAULT_PATH_SIZE = 100; + /** + * Flags that suppress the visibility of the 'new' dot or the app badge, for one reason or + * another. If any of these flags are set, the dot will not be shown. + * If {@link SuppressionFlag#BEHIND_STACK} then the app badge will not be shown. + */ + enum SuppressionFlag { + // TODO: (b/277815200) implement flyout + // Suppressed because the flyout is visible - it will morph into the dot via animation. + FLYOUT_VISIBLE, + // Suppressed because this bubble is behind others in the collapsed stack. + BEHIND_STACK, + } + + private final EnumSet<SuppressionFlag> mSuppressionFlags = + EnumSet.noneOf(SuppressionFlag.class); + private final ImageView mBubbleIcon; private final ImageView mAppIcon; private final int mBubbleSize; + private DotRenderer mDotRenderer; + private DotRenderer.DrawParams mDrawParams; + private int mDotColor; + private Rect mTempBounds = new Rect(); + + // Whether the dot is animating + private boolean mDotIsAnimating; + // What scale value the dot is animating to + private float mAnimatingToDotScale; + // The current scale value of the dot + private float mDotScale; + // TODO: (b/273310265) handle RTL + // Whether the bubbles are positioned on the left or right side of the screen private boolean mOnLeft = false; - private BubbleBarBubble mBubble; + private BubbleBarItem mBubble; public BubbleView(Context context) { this(context, null); @@ -74,6 +108,8 @@ public class BubbleView extends ConstraintLayout { mBubbleIcon = findViewById(R.id.icon_view); mAppIcon = findViewById(R.id.app_icon_view); + mDrawParams = new DotRenderer.DrawParams(); + setFocusable(true); setClickable(true); setOutlineProvider(new ViewOutlineProvider() { @@ -90,45 +126,147 @@ public class BubbleView extends ConstraintLayout { outline.setOval(inset, inset, inset + normalizedSize, inset + normalizedSize); } + @Override + public void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + + if (!shouldDrawDot()) { + return; + } + + getDrawingRect(mTempBounds); + + mDrawParams.dotColor = mDotColor; + mDrawParams.iconBounds = mTempBounds; + mDrawParams.leftAlign = mOnLeft; + mDrawParams.scale = mDotScale; + + mDotRenderer.draw(canvas, mDrawParams); + } + /** Sets the bubble being rendered in this view. */ void setBubble(BubbleBarBubble bubble) { mBubble = bubble; mBubbleIcon.setImageBitmap(bubble.getIcon()); mAppIcon.setImageBitmap(bubble.getBadge()); + mDotColor = bubble.getDotColor(); + mDotRenderer = new DotRenderer(mBubbleSize, bubble.getDotPath(), DEFAULT_PATH_SIZE); + } + + /** + * Sets that this bubble represents the overflow. The overflow appears in the list of bubbles + * but does not represent app content, instead it shows recent bubbles that couldn't fit into + * the list of bubbles. It doesn't show an app icon because it is part of system UI / doesn't + * come from an app. + */ + void setOverflow(BubbleBarOverflow overflow, Bitmap bitmap) { + mBubble = overflow; + mBubbleIcon.setImageBitmap(bitmap); + mAppIcon.setVisibility(GONE); // Overflow doesn't show the app badge } /** Returns the bubble being rendered in this view. */ @Nullable - BubbleBarBubble getBubble() { + BubbleBarItem getBubble() { return mBubble; } - /** Shows the app badge on this bubble. */ - void showBadge() { - Bitmap appBadgeBitmap = mBubble.getBadge(); - if (appBadgeBitmap == null) { - mAppIcon.setVisibility(GONE); + void updateDotVisibility(boolean animate) { + final float targetScale = shouldDrawDot() ? 1f : 0f; + if (animate) { + animateDotScale(); + } else { + mDotScale = targetScale; + mAnimatingToDotScale = targetScale; + invalidate(); + } + } + + void updateBadgeVisibility() { + if (mBubble instanceof BubbleBarOverflow) { + // The overflow bubble does not have a badge, so just bail. return; } + BubbleBarBubble bubble = (BubbleBarBubble) mBubble; + Bitmap appBadgeBitmap = bubble.getBadge(); + int translationX = mOnLeft + ? -(bubble.getIcon().getWidth() - appBadgeBitmap.getWidth()) + : 0; + mAppIcon.setTranslationX(translationX); + mAppIcon.setVisibility(isBehindStack() ? GONE : VISIBLE); + } - int translationX; - if (mOnLeft) { - translationX = -(mBubble.getIcon().getWidth() - appBadgeBitmap.getWidth()); + /** Sets whether this bubble is in the stack & not the first bubble. **/ + void setBehindStack(boolean behindStack, boolean animate) { + if (behindStack) { + mSuppressionFlags.add(SuppressionFlag.BEHIND_STACK); } else { - translationX = 0; + mSuppressionFlags.remove(SuppressionFlag.BEHIND_STACK); } + updateDotVisibility(animate); + updateBadgeVisibility(); + } - mAppIcon.setTranslationX(translationX); - mAppIcon.setVisibility(VISIBLE); + /** Whether this bubble is in the stack & not the first bubble. **/ + boolean isBehindStack() { + return mSuppressionFlags.contains(SuppressionFlag.BEHIND_STACK); + } + + /** Whether the dot indicating unseen content in a bubble should be shown. */ + private boolean shouldDrawDot() { + boolean bubbleHasUnseenContent = mBubble != null + && mBubble instanceof BubbleBarBubble + && mSuppressionFlags.isEmpty() + && !((BubbleBarBubble) mBubble).getInfo().isNotificationSuppressed(); + + // Always render the dot if it's animating, since it could be animating out. Otherwise, show + // it if the bubble wants to show it, and we aren't suppressing it. + return bubbleHasUnseenContent || mDotIsAnimating; } - /** Hides the app badge on this bubble. */ - void hideBadge() { - mAppIcon.setVisibility(GONE); + /** How big the dot should be, fraction from 0 to 1. */ + private void setDotScale(float fraction) { + mDotScale = fraction; + invalidate(); } + /** + * Animates the dot to the given scale. + */ + private void animateDotScale() { + float toScale = shouldDrawDot() ? 1f : 0f; + mDotIsAnimating = true; + + // Don't restart the animation if we're already animating to the given value. + if (mAnimatingToDotScale == toScale || !shouldDrawDot()) { + mDotIsAnimating = false; + return; + } + + mAnimatingToDotScale = toScale; + + final boolean showDot = toScale > 0f; + + // Do NOT wait until after animation ends to setShowDot + // to avoid overriding more recent showDot states. + clearAnimation(); + animate() + .setDuration(200) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .setUpdateListener((valueAnimator) -> { + float fraction = valueAnimator.getAnimatedFraction(); + fraction = showDot ? fraction : 1f - fraction; + setDotScale(fraction); + }).withEndAction(() -> { + setDotScale(showDot ? 1f : 0f); + mDotIsAnimating = false; + }).start(); + } + + @Override public String toString() { - return "BubbleView{" + mBubble + "}"; + String toString = mBubble != null ? mBubble.getKey() : "null"; + return "BubbleView{" + toString + "}"; } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/OWNERS b/quickstep/src/com/android/launcher3/taskbar/bubbles/OWNERS new file mode 100644 index 0000000000..7af0389997 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/OWNERS @@ -0,0 +1 @@ +madym@google.com diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt index e704e5116b..b6820817c8 100644 --- a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt @@ -17,10 +17,12 @@ package com.android.launcher3.taskbar.navbutton import android.content.res.Resources +import android.graphics.drawable.RotateDrawable import android.view.ViewGroup import android.widget.ImageView import android.widget.LinearLayout import com.android.launcher3.R +import com.android.launcher3.Utilities import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter /** @@ -40,7 +42,18 @@ abstract class AbstractNavButtonLayoutter( protected val endContextualContainer: ViewGroup, protected val startContextualContainer: ViewGroup ) : NavButtonLayoutter { - protected val homeButton: ImageView = navButtonContainer.requireViewById(R.id.home) - protected val recentsButton: ImageView = navButtonContainer.requireViewById(R.id.recent_apps) - protected val backButton: ImageView = navButtonContainer.requireViewById(R.id.back) + protected val homeButton: ImageView? = navButtonContainer.findViewById(R.id.home) + protected val recentsButton: ImageView? = navButtonContainer.findViewById(R.id.recent_apps) + protected val backButton: ImageView? = navButtonContainer.findViewById(R.id.back) + + init { + // setup back button drawable + if (backButton != null) { + val rotateDrawable = RotateDrawable() + rotateDrawable.drawable = backButton.context?.getDrawable(R.drawable.ic_sysbar_back) + rotateDrawable.fromDegrees = 0f + rotateDrawable.toDegrees = if (Utilities.isRtl(backButton.resources)) 90f else -90f + backButton.setImageDrawable(rotateDrawable) + } + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt index 468a1a7093..4a53c0c3b1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt @@ -51,10 +51,10 @@ class KidsNavLayoutter( val paddingTop = (buttonHeight - iconSize) / 2 // Update icons - backButton.setImageDrawable(backButton.context.getDrawable(DRAWABLE_SYSBAR_BACK_KIDS)) + backButton!!.setImageDrawable(backButton.context.getDrawable(DRAWABLE_SYSBAR_BACK_KIDS)) backButton.scaleType = ImageView.ScaleType.FIT_CENTER backButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop) - homeButton.setImageDrawable(homeButton.getContext().getDrawable(DRAWABLE_SYSBAR_HOME_KIDS)) + homeButton!!.setImageDrawable(homeButton.context.getDrawable(DRAWABLE_SYSBAR_HOME_KIDS)) homeButton.scaleType = ImageView.ScaleType.FIT_CENTER homeButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop) diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt index 000778d9df..931f692fa2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt @@ -17,6 +17,8 @@ package com.android.launcher3.taskbar.navbutton import android.content.res.Resources +import android.view.Surface.ROTATION_90 +import android.view.Surface.Rotation import android.view.ViewGroup import android.widget.FrameLayout import android.widget.LinearLayout @@ -56,7 +58,8 @@ class NavButtonLayoutFactory { isKidsMode: Boolean, isInSetup: Boolean, isThreeButtonNav: Boolean, - phoneMode: Boolean + phoneMode: Boolean, + @Rotation surfaceRotation: Int ): NavButtonLayoutter { val navButtonContainer = navButtonsView.requireViewById<LinearLayout>(ID_END_NAV_BUTTONS) @@ -65,6 +68,7 @@ class NavButtonLayoutFactory { val startContextualContainer = navButtonsView.requireViewById<ViewGroup>(ID_START_CONTEXTUAL_BUTTONS) val isPhoneNavMode = phoneMode && isThreeButtonNav + val isPhoneGestureMode = phoneMode && !isThreeButtonNav return when { isPhoneNavMode -> { if (!deviceProfile.isLandscape) { @@ -74,15 +78,30 @@ class NavButtonLayoutFactory { endContextualContainer, startContextualContainer ) - } else { + } else if (surfaceRotation == ROTATION_90) { PhoneLandscapeNavLayoutter( resources, navButtonContainer, endContextualContainer, startContextualContainer ) + } else { + PhoneSeascapeNavLayoutter( + resources, + navButtonContainer, + endContextualContainer, + startContextualContainer + ) } } + isPhoneGestureMode ->{ + PhoneGestureLayoutter( + resources, + navButtonContainer, + endContextualContainer, + startContextualContainer + ) + } deviceProfile.isTaskbarPresent -> { return when { isInSetup -> { diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt new file mode 100644 index 0000000000..8525c6c90c --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt @@ -0,0 +1,41 @@ +/* + * 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.launcher3.taskbar.navbutton + +import android.content.res.Resources +import android.view.ViewGroup +import android.widget.LinearLayout +import com.android.launcher3.DeviceProfile + +/** Layoutter for showing gesture navigation on phone screen. No buttons here, no-op container */ +class PhoneGestureLayoutter( + resources: Resources, + navBarContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : + AbstractNavButtonLayoutter( + resources, + navBarContainer, + endContextualContainer, + startContextualContainer + ) { + + override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { + // no-op + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt index 201895fc67..2acd5d4eff 100644 --- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt @@ -27,7 +27,7 @@ import com.android.launcher3.R import com.android.launcher3.taskbar.TaskbarManager import com.android.launcher3.util.DimensionUtils -class PhoneLandscapeNavLayoutter( +open class PhoneLandscapeNavLayoutter( resources: Resources, navBarContainer: LinearLayout, endContextualContainer: ViewGroup, @@ -42,17 +42,15 @@ class PhoneLandscapeNavLayoutter( override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { // TODO(b/230395757): Polish pending, this is just to make it usable - val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) - val taskbarDimensions = - DimensionUtils.getTaskbarPhoneDimensions(dp, resources, TaskbarManager.isPhoneMode(dp)) + val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources, + TaskbarManager.isPhoneMode(dp)) navButtonContainer.removeAllViews() navButtonContainer.orientation = LinearLayout.VERTICAL + val navContainerParams = FrameLayout.LayoutParams( + taskbarDimensions.x, ViewGroup.LayoutParams.MATCH_PARENT) navContainerParams.apply { - width = taskbarDimensions.x - height = ViewGroup.LayoutParams.MATCH_PARENT - gravity = Gravity.CENTER topMargin = endStartMargins bottomMargin = endStartMargins marginEnd = 0 @@ -65,6 +63,7 @@ class PhoneLandscapeNavLayoutter( navButtonContainer.addView(backButton) navButtonContainer.layoutParams = navContainerParams + navButtonContainer.gravity = Gravity.CENTER // Add the spaces in between the nav buttons val spaceInBetween: Int = diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt index f7ac9745f0..c76311582d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt @@ -41,27 +41,31 @@ class PhonePortraitNavLayoutter( override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { // TODO(b/230395757): Polish pending, this is just to make it usable - val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams val taskbarDimensions = - DimensionUtils.getTaskbarPhoneDimensions(dp, resources, TaskbarManager.isPhoneMode(dp)) + DimensionUtils.getTaskbarPhoneDimensions(dp, resources, + TaskbarManager.isPhoneMode(dp)) val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) - navContainerParams.width = taskbarDimensions.x - navContainerParams.height = ViewGroup.LayoutParams.MATCH_PARENT - navContainerParams.gravity = Gravity.CENTER_VERTICAL // Ensure order of buttons is correct navButtonContainer.removeAllViews() navButtonContainer.orientation = LinearLayout.HORIZONTAL - navContainerParams.topMargin = 0 - navContainerParams.bottomMargin = 0 - navContainerParams.marginEnd = endStartMargins - navContainerParams.marginStart = endStartMargins + + val navContainerParams = FrameLayout.LayoutParams( + taskbarDimensions.x, ViewGroup.LayoutParams.MATCH_PARENT) + navContainerParams.apply { + topMargin = 0 + bottomMargin = 0 + marginEnd = endStartMargins + marginStart = endStartMargins + } + // Swap recents and back button in case we were landscape prior to this navButtonContainer.addView(backButton) navButtonContainer.addView(homeButton) navButtonContainer.addView(recentsButton) navButtonContainer.layoutParams = navContainerParams + navButtonContainer.gravity = Gravity.CENTER // Add the spaces in between the nav buttons val spaceInBetween = diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt new file mode 100644 index 0000000000..f0fe58197b --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt @@ -0,0 +1,46 @@ +/* +* 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.launcher3.taskbar.navbutton + +import android.content.res.Resources +import android.view.ViewGroup +import android.widget.LinearLayout +import com.android.launcher3.DeviceProfile + +class PhoneSeascapeNavLayoutter( + resources: Resources, + navBarContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : + PhoneLandscapeNavLayoutter( + resources, + navBarContainer, + endContextualContainer, + startContextualContainer + ) { + + override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { + // TODO(b/230395757): Polish pending, this is just to make it usable + super.layoutButtons(dp, isContextualButtonShowing) + navButtonContainer.removeAllViews() + // Flip ordering of back and recents buttons + navButtonContainer.addView(backButton) + navButtonContainer.addView(homeButton) + navButtonContainer.addView(recentsButton) + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt index 5ec7ca0e2e..8332b7dd74 100644 --- a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt @@ -40,7 +40,6 @@ class TaskbarNavLayoutter( override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { // Add spacing after the end of the last nav button - val navButtonParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams var navMarginEnd = resources.getDimension(dp.inv.inlineNavButtonsEndSpacing).toInt() val contextualWidth = endContextualContainer.width // If contextual buttons are showing, we check if the end margin is enough for the @@ -50,10 +49,10 @@ class TaskbarNavLayoutter( navMarginEnd += resources.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2 } + val navButtonParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT) navButtonParams.apply { - gravity = Gravity.END - width = FrameLayout.LayoutParams.WRAP_CONTENT - height = ViewGroup.LayoutParams.MATCH_PARENT + gravity = Gravity.END or Gravity.CENTER_VERTICAL marginEnd = navMarginEnd } navButtonContainer.orientation = LinearLayout.HORIZONTAL diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java index a642693137..64cc47c739 100644 --- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java @@ -18,6 +18,8 @@ package com.android.launcher3.taskbar.overlay; import android.content.Context; import android.view.View; +import androidx.annotation.Nullable; + import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.dot.DotInfo; @@ -29,6 +31,7 @@ import com.android.launcher3.taskbar.TaskbarControllers; import com.android.launcher3.taskbar.TaskbarDragController; import com.android.launcher3.taskbar.TaskbarUIController; import com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView; +import com.android.launcher3.taskbar.allapps.TaskbarSearchSessionController; import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource; /** @@ -47,6 +50,8 @@ public class TaskbarOverlayContext extends BaseTaskbarContext { private final int mStashedTaskbarHeight; private final TaskbarUIController mUiController; + private @Nullable TaskbarSearchSessionController mSearchSessionController; + public TaskbarOverlayContext( Context windowContext, TaskbarActivityContext taskbarContext, @@ -62,6 +67,15 @@ public class TaskbarOverlayContext extends BaseTaskbarContext { mUiController = controllers.uiController; } + public @Nullable TaskbarSearchSessionController getSearchSessionController() { + return mSearchSessionController; + } + + public void setSearchSessionController( + @Nullable TaskbarSearchSessionController searchSessionController) { + mSearchSessionController = searchSessionController; + } + int getStashedTaskbarHeight() { return mStashedTaskbarHeight; } @@ -112,6 +126,11 @@ public class TaskbarOverlayContext extends BaseTaskbarContext { } @Override + public View.OnLongClickListener getAllAppsItemLongClickListener() { + return mDragController::startDragOnLongClick; + } + + @Override public PopupDataProvider getPopupDataProvider() { return mTaskbarContext.getPopupDataProvider(); } diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java index add72791d4..ff00560956 100644 --- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java +++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java @@ -21,6 +21,7 @@ import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_ import android.content.Context; import android.graphics.Insets; +import android.media.permission.SafeCloseable; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -29,6 +30,7 @@ import android.view.WindowInsets; import androidx.annotation.NonNull; +import com.android.app.viewcapture.SettingsAwareViewCapture; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; @@ -44,6 +46,7 @@ public class TaskbarOverlayDragLayer extends BaseDragLayer<TaskbarOverlayContext> implements ViewTreeObserver.OnComputeInternalInsetsListener { + private SafeCloseable mViewCaptureCloseable; private final List<OnClickListener> mOnClickListeners = new CopyOnWriteArrayList<>(); private final TouchController mClickListenerTouchController = new TouchController() { @Override @@ -77,12 +80,15 @@ public class TaskbarOverlayDragLayer extends protected void onAttachedToWindow() { super.onAttachedToWindow(); getViewTreeObserver().addOnComputeInternalInsetsListener(this); + mViewCaptureCloseable = SettingsAwareViewCapture.getInstance(getContext()) + .startCapture(getRootView(), ".TaskbarOverlay"); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); getViewTreeObserver().removeOnComputeInternalInsetsListener(this); + mViewCaptureCloseable.close(); } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java index a53dc1545f..475f465d0e 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java +++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java @@ -16,13 +16,16 @@ package com.android.launcher3.uioverrides; +import android.app.ActivityOptions; import android.app.Person; import android.content.Context; import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; +import android.window.RemoteTransition; import com.android.launcher3.Utilities; +import com.android.quickstep.util.FadeOutRemoteTransition; import java.util.Map; @@ -41,4 +44,13 @@ public class ApiWrapper { public static Map<String, LauncherActivityInfo> getActivityOverrides(Context context) { return context.getSystemService(LauncherApps.class).getActivityOverrides(); } + + /** + * Creates an ActivityOptions to play fade-out animation on closing targets + */ + public static ActivityOptions createFadeOutAnimOptions(Context context) { + ActivityOptions options = ActivityOptions.makeBasic(); + options.setRemoteTransition(new RemoteTransition(new FadeOutRemoteTransition())); + return options; + } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index 955440b49a..d78ca88249 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -16,40 +16,34 @@ package com.android.launcher3.uioverrides; +import static com.android.app.animation.Interpolators.AGGRESSIVE_EASE_IN_OUT; +import static com.android.app.animation.Interpolators.FINAL_FRAME; +import static com.android.app.animation.Interpolators.INSTANT; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.LauncherState.QUICK_SWITCH_FROM_HOME; -import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT; -import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; -import static com.android.launcher3.anim.Interpolators.INSTANT; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y; import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; -import static com.android.quickstep.views.FloatingTaskView.PRIMARY_TRANSLATE_OFFSCREEN; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET; import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; import static com.android.quickstep.views.RecentsView.TASK_THUMBNAIL_SPLASH_ALPHA; -import android.graphics.Rect; -import android.graphics.RectF; import android.util.FloatProperty; import android.view.animation.Interpolator; import androidx.annotation.NonNull; import com.android.launcher3.LauncherState; -import com.android.launcher3.Utilities; import com.android.launcher3.anim.PendingAnimation; -import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.states.StateAnimationConfig; -import com.android.quickstep.views.FloatingTaskView; import com.android.quickstep.views.RecentsView; /** @@ -113,48 +107,19 @@ public abstract class BaseRecentsViewStateController<T extends RecentsView> setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f, config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR)); - if (mRecentsView.isSplitSelectionActive()) { - // TODO (b/238651489): Refactor state management to avoid need for double check - FloatingTaskView floatingTask = mRecentsView.getFirstFloatingTaskView(); - if (floatingTask != null) { - // We are in split selection state currently, transitioning to another state - DragLayer dragLayer = mLauncher.getDragLayer(); - RectF onScreenRectF = new RectF(); - Utilities.getBoundsForViewInDragLayer(mLauncher.getDragLayer(), floatingTask, - new Rect(0, 0, floatingTask.getWidth(), floatingTask.getHeight()), - false, null, onScreenRectF); - // Get the part of the floatingTask that intersects with the DragLayer (i.e. the - // on-screen portion) - onScreenRectF.intersect( - dragLayer.getLeft(), - dragLayer.getTop(), - dragLayer.getRight(), - dragLayer.getBottom() - ); - - setter.setFloat( - mRecentsView.getFirstFloatingTaskView(), - PRIMARY_TRANSLATE_OFFSCREEN, - mRecentsView.getPagedOrientationHandler() - .getFloatingTaskOffscreenTranslationTarget( - floatingTask, - onScreenRectF, - floatingTask.getStagePosition(), - mLauncher.getDeviceProfile() - ), - config.getInterpolator( - ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN, - LINEAR - )); - setter.setViewAlpha( - mRecentsView.getSplitInstructionsView(), - 0, - config.getInterpolator( - ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE, - LINEAR - ) - ); - } + boolean exitingOverview = !FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get() + && !toState.overviewUi; + if (mRecentsView.isSplitSelectionActive() && exitingOverview) { + setter.add(mRecentsView.getSplitSelectController().getSplitAnimationController() + .createPlaceholderDismissAnim(mLauncher)); + setter.setViewAlpha( + mRecentsView.getSplitInstructionsView(), + 0, + config.getInterpolator( + ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE, + LINEAR + ) + ); } setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0, diff --git a/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java b/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java deleted file mode 100644 index d8aa235823..0000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java +++ /dev/null @@ -1,159 +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 com.android.launcher3.uioverrides; - -import static android.os.IBinder.FLAG_ONEWAY; - -import android.os.Binder; -import android.os.Build; -import android.os.IBinder; -import android.os.Looper; -import android.os.RemoteException; -import android.util.Log; - -import androidx.annotation.MainThread; - -import java.util.HashSet; -import java.util.Locale; -import java.util.function.BiConsumer; -import java.util.function.Supplier; - -/** - * A binder proxy transaction listener for tracking non-whitelisted binder calls. - */ -public class DejankBinderTracker implements Binder.ProxyTransactListener { - private static final String TAG = "DejankBinderTracker"; - - private static final Object sLock = new Object(); - private static final HashSet<String> sWhitelistedFrameworkClasses = new HashSet<>(); - static { - // Common IPCs that are ok to block the main thread. - sWhitelistedFrameworkClasses.add("android.view.IWindowSession"); - sWhitelistedFrameworkClasses.add("android.os.IPowerManager"); - } - private static boolean sTemporarilyIgnoreTracking = false; - - // Used by the client to limit binder tracking to specific regions - private static boolean sTrackingAllowed = false; - - private BiConsumer<String, Integer> mUnexpectedTransactionCallback; - private boolean mIsTracking = false; - - /** - * Temporarily ignore blocking binder calls for the duration of this {@link Runnable}. - */ - @MainThread - public static void whitelistIpcs(Runnable runnable) { - sTemporarilyIgnoreTracking = true; - runnable.run(); - sTemporarilyIgnoreTracking = false; - } - - /** - * Temporarily ignore blocking binder calls for the duration of this {@link Supplier}. - */ - @MainThread - public static <T> T whitelistIpcs(Supplier<T> supplier) { - sTemporarilyIgnoreTracking = true; - T value = supplier.get(); - sTemporarilyIgnoreTracking = false; - return value; - } - - /** - * Enables binder tracking during a test. - */ - @MainThread - public static void allowBinderTrackingInTests() { - sTrackingAllowed = true; - } - - /** - * Disables binder tracking during a test. - */ - @MainThread - public static void disallowBinderTrackingInTests() { - sTrackingAllowed = false; - } - - public DejankBinderTracker(BiConsumer<String, Integer> unexpectedTransactionCallback) { - mUnexpectedTransactionCallback = unexpectedTransactionCallback; - } - - @MainThread - public void startTracking() { - if (!Build.TYPE.toLowerCase(Locale.ROOT).contains("debug") - && !Build.TYPE.toLowerCase(Locale.ROOT).equals("eng")) { - Log.wtf(TAG, "Unexpected use of binder tracker in non-debug build", new Exception()); - return; - } - if (mIsTracking) { - return; - } - mIsTracking = true; - Binder.setProxyTransactListener(this); - } - - @MainThread - public void stopTracking() { - if (!mIsTracking) { - return; - } - mIsTracking = false; - Binder.setProxyTransactListener(null); - } - - // Override the hidden Binder#onTransactStarted method - public synchronized Object onTransactStarted(IBinder binder, int transactionCode, int flags) { - if (!mIsTracking - || !sTrackingAllowed - || sTemporarilyIgnoreTracking - || (flags & FLAG_ONEWAY) == FLAG_ONEWAY - || !isMainThread()) { - return null; - } - - String descriptor; - try { - descriptor = binder.getInterfaceDescriptor(); - if (sWhitelistedFrameworkClasses.contains(descriptor)) { - return null; - } - } catch (RemoteException e) { - e.printStackTrace(); - descriptor = binder.getClass().getSimpleName(); - } - - mUnexpectedTransactionCallback.accept(descriptor, transactionCode); - return null; - } - - @Override - public Object onTransactStarted(IBinder binder, int transactionCode) { - // Do nothing - return null; - } - - @Override - public void onTransactEnded(Object session) { - // Do nothing - } - - public static boolean isMainThread() { - return Thread.currentThread() == Looper.getMainLooper().getThread(); - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java index 3e1d0f137a..2064fe22f6 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java +++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java @@ -15,7 +15,7 @@ */ package com.android.launcher3.uioverrides; -import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; +import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE; import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED; import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter; @@ -260,8 +260,8 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { Keyframe.ofFloat(0.82f, finalTrans - getOutlineOffsetY() / 2f), // Overshoot Keyframe.ofFloat(1f, finalTrans) // Ease back into the final position }; - keyframes[1].setInterpolator(ACCEL_DEACCEL); - keyframes[2].setInterpolator(ACCEL_DEACCEL); + keyframes[1].setInterpolator(ACCELERATE_DECELERATE); + keyframes[2].setInterpolator(ACCELERATE_DECELERATE); mSlotMachineAnim = ObjectAnimator.ofPropertyValuesHolder(this, PropertyValuesHolder.ofKeyframe(SLOT_MACHINE_TRANSLATION_Y, keyframes)); @@ -337,7 +337,6 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { if (getTag() instanceof WorkspaceItemInfo) { WorkspaceItemInfo info = (WorkspaceItemInfo) getTag(); isBadged = !Process.myUserHandle().equals(info.user) - || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; } diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 3139e4d91a..5a46b8d8e9 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -20,23 +20,20 @@ import static android.os.Trace.TRACE_TAG_APP; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE; import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED; +import static com.android.app.animation.Interpolators.EMPHASIZED; import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON; import static com.android.launcher3.LauncherSettings.Animation.VIEW_BACKGROUND; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; -import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NO_OFFSET; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK; import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED; import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; -import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE; import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE; -import static com.android.launcher3.config.FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP; import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition; @@ -60,8 +57,6 @@ import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SY import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; -import android.animation.ValueAnimator; -import android.app.ActivityManager; import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; @@ -71,20 +66,20 @@ import android.content.res.Configuration; import android.graphics.Color; import android.graphics.Rect; import android.graphics.RectF; -import android.hardware.SensorManager; -import android.hardware.devicestate.DeviceStateManager; import android.hardware.display.DisplayManager; import android.media.permission.SafeCloseable; import android.os.Build; import android.os.Bundle; -import android.os.CancellationSignal; import android.os.IBinder; import android.os.SystemProperties; import android.os.Trace; +import android.util.AttributeSet; +import android.util.Log; import android.view.Display; import android.view.HapticFeedbackConstants; -import android.view.RemoteAnimationTarget; import android.view.View; +import android.widget.AnalogClock; +import android.widget.TextClock; import android.window.BackEvent; import android.window.OnBackAnimationCallback; import android.window.OnBackInvokedDispatcher; @@ -111,7 +106,6 @@ import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.appprediction.PredictionRowView; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.hybridhotseat.HotseatPredictionController; import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.StatsLogManager; @@ -119,9 +113,9 @@ import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.WellbeingModel; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.proxy.ProxyActivityStarter; -import com.android.launcher3.proxy.StartActivityParams; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statehandlers.DesktopVisibilityController; import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory; @@ -153,6 +147,7 @@ import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource; +import com.android.launcher3.util.StartActivityParams; import com.android.launcher3.util.TouchController; import com.android.launcher3.widget.LauncherWidgetHolder; import com.android.quickstep.OverviewCommandHelper; @@ -160,12 +155,10 @@ import com.android.quickstep.RecentsModel; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskUtils; import com.android.quickstep.TouchInteractionService.TISBinder; +import com.android.quickstep.util.AsyncClockEventDelegate; import com.android.quickstep.util.GroupTask; import com.android.quickstep.util.LauncherUnfoldAnimationController; -import com.android.quickstep.util.ProxyScreenStatusProvider; import com.android.quickstep.util.QuickstepOnboardingPrefs; -import com.android.quickstep.util.RemoteAnimationProvider; -import com.android.quickstep.util.RemoteFadeOutAnimationListener; import com.android.quickstep.util.SplitSelectStateController; import com.android.quickstep.util.SplitToWorkspaceController; import com.android.quickstep.util.SplitWithKeyboardShortcutController; @@ -175,16 +168,14 @@ import com.android.quickstep.views.FloatingTaskView; import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.unfold.RemoteUnfoldSharedComponent; -import com.android.systemui.unfold.UnfoldSharedComponent; import com.android.systemui.unfold.UnfoldTransitionFactory; import com.android.systemui.unfold.UnfoldTransitionProgressProvider; import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig; import com.android.systemui.unfold.config.UnfoldTransitionConfig; import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver; -import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider; -import com.android.systemui.unfold.system.DeviceStateManagerFoldProvider; import com.android.systemui.unfold.updates.RotationChangeProvider; import java.io.FileDescriptor; @@ -194,16 +185,22 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; public class QuickstepLauncher extends Launcher { + private static final boolean TRACE_LAYOUTS = + SystemProperties.getBoolean("persist.debug.trace_layouts", false); + private static final String TRACE_RELAYOUT_CLASS = + SystemProperties.get("persist.debug.trace_request_layout_class", null); - public static final boolean ENABLE_PIP_KEEP_CLEAR_ALGORITHM = - SystemProperties.getBoolean("persist.wm.debug.enable_pip_keep_clear_algorithm", true); + private static final String TAG = "QuickstepLauncher"; public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false; + protected static final String RING_APPEAR_ANIMATION_PREFIX = "RingAppearAnimation\t"; + private FixedContainerItems mAllAppsPredictions; private HotseatPredictionController mHotseatPredictionController; private DepthController mDepthController; @@ -211,11 +208,8 @@ public class QuickstepLauncher extends Launcher { private QuickstepTransitionManager mAppTransitionManager; private OverviewActionsView mActionsView; private TISBindHelper mTISBindHelper; - private @Nullable TaskbarManager mTaskbarManager; - private @Nullable OverviewCommandHelper mOverviewCommandHelper; private @Nullable LauncherTaskbarUIController mTaskbarUIController; // Will be updated when dragging from taskbar. - private @Nullable DragOptions mNextWorkspaceDragOptions = null; private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider; private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController; @@ -223,6 +217,8 @@ public class QuickstepLauncher extends Launcher { private SplitWithKeyboardShortcutController mSplitWithKeyboardShortcutController; private SplitToWorkspaceController mSplitToWorkspaceController; + private AsyncClockEventDelegate mAsyncClockEventDelegate; + /** * If Launcher restarted while in the middle of an Overview split select, it needs this data to * recover. In all other cases this will remain null. @@ -258,6 +254,10 @@ public class QuickstepLauncher extends Launcher { mTISBindHelper = new TISBindHelper(this, this::onTISConnected); mDepthController = new DepthController(this); mDesktopVisibilityController = new DesktopVisibilityController(this); + if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) { + mDesktopVisibilityController.registerSystemUiListener(); + mSplitSelectStateController.initSplitFromDesktopController(this); + } mHotseatPredictionController = new HotseatPredictionController(this); mEnableWidgetDepth = SystemProperties.getBoolean("ro.launcher.depth.widget", true); @@ -279,7 +279,6 @@ public class QuickstepLauncher extends Launcher { if (mAllAppsPredictions != null && (info.itemType == ITEM_TYPE_APPLICATION - || info.itemType == ITEM_TYPE_SHORTCUT || info.itemType == ITEM_TYPE_DEEP_SHORTCUT)) { int count = mAllAppsPredictions.items.size(); for (int i = 0; i < count; i++) { @@ -349,9 +348,7 @@ public class QuickstepLauncher extends Launcher { mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null); RunnableList result = super.startActivitySafely(v, intent, item); if (result == null) { - if (getTaskbarUIController() == null) { - mHotseatPredictionController.setPauseUIUpdate(false); - } + mHotseatPredictionController.setPauseUIUpdate(false); } else { result.add(() -> mHotseatPredictionController.setPauseUIUpdate(false)); } @@ -406,8 +403,7 @@ public class QuickstepLauncher extends Launcher { } private List<SystemShortcut.Factory<QuickstepLauncher>> getSplitShortcuts() { - - if (!ENABLE_SPLIT_FROM_WORKSPACE.get() || !mDeviceProfile.isTablet) { + if (!mDeviceProfile.isTablet || mSplitSelectStateController.isSplitSelectActive()) { return Collections.emptyList(); } RecentsView recentsView = getOverviewPanel(); @@ -435,12 +431,8 @@ public class QuickstepLauncher extends Launcher { boolean visible = (state == NORMAL || state == OVERVIEW) && (willUserBeActive || isUserActive()) && !profile.isVerticalBarLayout(); - if (ENABLE_PIP_KEEP_CLEAR_ALGORITHM) { - SystemUiProxy.INSTANCE.get(this) - .setLauncherKeepClearAreaHeight(visible, profile.hotseatBarSizePx); - } else { - SystemUiProxy.INSTANCE.get(this).setShelfHeight(visible, profile.hotseatBarSizePx); - } + SystemUiProxy.INSTANCE.get(this) + .setLauncherKeepClearAreaHeight(visible, profile.hotseatBarSizePx); } if (state == NORMAL && !inTransition) { ((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false); @@ -449,6 +441,7 @@ public class QuickstepLauncher extends Launcher { @Override public void bindExtraContainerItems(FixedContainerItems item) { + Log.d(TAG, "Bind extra container items"); if (item.containerId == Favorites.CONTAINER_PREDICTION) { mAllAppsPredictions = item; PredictionRowView<?> predictionRowView = @@ -456,6 +449,7 @@ public class QuickstepLauncher extends Launcher { PredictionRowView.class); predictionRowView.setPredictedApps(item.items); } else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) { + Log.d(TAG, "Bind extra container item is hotseat prediction"); mHotseatPredictionController.setPredictedItems(item); } else if (item.containerId == Favorites.CONTAINER_WIDGETS_PREDICTION) { getPopupDataProvider().setRecommendedWidgets(item.items); @@ -472,22 +466,28 @@ public class QuickstepLauncher extends Launcher { public void onDestroy() { mAppTransitionManager.onActivityDestroyed(); if (mUnfoldTransitionProgressProvider != null) { - if (FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) { - SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null); - } - + SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null); mUnfoldTransitionProgressProvider.destroy(); } mTISBindHelper.onDestroy(); - if (mTaskbarManager != null) { - mTaskbarManager.clearActivity(this); - } if (mLauncherUnfoldAnimationController != null) { mLauncherUnfoldAnimationController.onDestroy(); } + if (mDesktopVisibilityController != null) { + mDesktopVisibilityController.unregisterSystemUiListener(); + } + + if (mSplitSelectStateController != null) { + mSplitSelectStateController.onDestroy(); + } + + if (mAsyncClockEventDelegate != null) { + mAsyncClockEventDelegate.onDestroy(); + } + super.onDestroy(); mHotseatPredictionController.destroy(); mSplitWithKeyboardShortcutController.onDestroy(); @@ -521,7 +521,7 @@ public class QuickstepLauncher extends Launcher { } case QUICK_SWITCH_STATE_ORDINAL: { RecentsView rv = getOverviewPanel(); - TaskView tasktolaunch = rv.getTaskViewAt(0); + TaskView tasktolaunch = rv.getCurrentPageTaskView(); if (tasktolaunch != null) { tasktolaunch.launchTask(success -> { if (!success) { @@ -545,11 +545,14 @@ public class QuickstepLauncher extends Launcher { ArrayList<TouchController> list = new ArrayList<>(); list.add(getDragController()); + Consumer<AnimatorSet> splitAnimator = animatorSet -> + animatorSet.play(mSplitSelectStateController.getSplitAnimationController() + .createPlaceholderDismissAnim(this)); switch (mode) { case NO_BUTTON: list.add(new NoButtonQuickSwitchTouchController(this)); - list.add(new NavBarToHomeTouchController(this)); - list.add(new NoButtonNavbarToOverviewTouchController(this)); + list.add(new NavBarToHomeTouchController(this, splitAnimator)); + list.add(new NoButtonNavbarToOverviewTouchController(this, splitAnimator)); break; case TWO_BUTTONS: list.add(new TwoButtonNavbarTouchController(this)); @@ -559,8 +562,14 @@ public class QuickstepLauncher extends Launcher { list.add(new PortraitStatesTouchController(this)); break; case THREE_BUTTONS: + list.add(new NoButtonQuickSwitchTouchController(this)); + list.add(new NavBarToHomeTouchController(this, splitAnimator)); + list.add(new NoButtonNavbarToOverviewTouchController(this, splitAnimator)); + list.add(new PortraitStatesTouchController(this)); + break; default: list.add(new PortraitStatesTouchController(this)); + break; } if (!getDeviceProfile().isMultiWindowMode) { @@ -601,6 +610,8 @@ public class QuickstepLauncher extends Launcher { mViewCapture = SettingsAwareViewCapture.getInstance(this).startCapture(getWindow()); } getWindow().addPrivateFlags(PRIVATE_FLAG_OPTIMIZE_MEASURE); + View.setTraceLayoutSteps(TRACE_LAYOUTS); + View.setTracedRequestLayoutClassClass(TRACE_RELAYOUT_CLASS); } @Override @@ -608,12 +619,14 @@ public class QuickstepLauncher extends Launcher { RecentsView recentsView = getOverviewPanel(); // Check if there is already an instance of this app running, if so, initiate the split // using that. - mSplitSelectStateController.findLastActiveTaskAndRunCallback( - splitSelectSource.itemInfo.getComponentKey(), - foundTask -> { - splitSelectSource.alreadyRunningTaskId = foundTask == null - ? INVALID_TASK_ID - : foundTask.key.id; + mSplitSelectStateController.findLastActiveTasksAndRunCallback( + Collections.singletonList(splitSelectSource.itemInfo.getComponentKey()), + foundTasks -> { + @Nullable Task foundTask = foundTasks.get(0); + boolean taskWasFound = foundTask != null; + splitSelectSource.alreadyRunningTaskId = taskWasFound + ? foundTask.key.id + : INVALID_TASK_ID; if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { startSplitToHome(splitSelectSource); } else { @@ -653,9 +666,13 @@ public class QuickstepLauncher extends Launcher { @Override public void onAnimationCancel(Animator animation) { getDragLayer().removeView(floatingTaskView); + mSplitSelectStateController.getSplitAnimationController() + .removeSplitInstructionsView(QuickstepLauncher.this); mSplitSelectStateController.resetState(); } }); + anim.add(mSplitSelectStateController.getSplitAnimationController() + .getShowSplitInstructionsAnim(this).buildAnim()); anim.buildAnim().start(); } @@ -681,9 +698,9 @@ public class QuickstepLauncher extends Launcher { @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - - if (mOverviewCommandHelper != null) { - mOverviewCommandHelper.clearPendingCommands(); + OverviewCommandHelper overviewCommandHelper = mTISBindHelper.getOverviewCommandHelper(); + if (overviewCommandHelper != null) { + overviewCommandHelper.clearPendingCommands(); } } @@ -806,8 +823,9 @@ public class QuickstepLauncher extends Launcher { } private void onTaskbarInAppDisplayProgressUpdate(float progress, int flag) { - if (mTaskbarManager == null - || mTaskbarManager.getCurrentActivityContext() == null + TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager(); + if (taskbarManager == null + || taskbarManager.getCurrentActivityContext() == null || mTaskbarUIController == null) { return; } @@ -851,7 +869,7 @@ public class QuickstepLauncher extends Launcher { if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) { DesktopVisibilityController controller = mDesktopVisibilityController; if (controller != null && controller.areFreeformTasksVisible() - && !controller.isGestureInProgress()) { + && !controller.isRecentsGestureInProgress()) { // Return early to skip setting activity to appear as resumed // TODO(b/255649902): shouldn't be needed when we have a separate launcher state // for desktop that we can use to control other parts of launcher @@ -879,11 +897,10 @@ public class QuickstepLauncher extends Launcher { } private void onTISConnected(TISBinder binder) { - mTaskbarManager = binder.getTaskbarManager(); - if (mTaskbarManager != null) { - mTaskbarManager.setActivity(this); + TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager(); + if (taskbarManager != null) { + taskbarManager.setActivity(this); } - mOverviewCommandHelper = binder.getOverviewCommandHelper(); } @Override @@ -894,43 +911,10 @@ public class QuickstepLauncher extends Launcher { private void initUnfoldTransitionProgressProvider() { final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig(); if (config.isEnabled()) { - if (RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) { - initRemotelyCalculatedUnfoldAnimation(config); - } else { - initLocallyCalculatedUnfoldAnimation(config); - } - + initRemotelyCalculatedUnfoldAnimation(config); } } - /** Registers hinge angle listener and calculates the animation progress in this process. */ - private void initLocallyCalculatedUnfoldAnimation(UnfoldTransitionConfig config) { - UnfoldSharedComponent unfoldComponent = - UnfoldTransitionFactory.createUnfoldSharedComponent( - /* context= */ this, - config, - ProxyScreenStatusProvider.INSTANCE, - new DeviceStateManagerFoldProvider( - getSystemService(DeviceStateManager.class), /* context= */ this), - new ActivityManagerActivityTypeProvider( - getSystemService(ActivityManager.class)), - getSystemService(SensorManager.class), - getMainThreadHandler(), - getMainExecutor(), - /* backgroundExecutor= */ UI_HELPER_EXECUTOR, - /* tracingTagPrefix= */ "launcher", - getSystemService(DisplayManager.class) - ); - - mUnfoldTransitionProgressProvider = unfoldComponent.getUnfoldTransitionProvider() - .orElseThrow(() -> new IllegalStateException( - "Trying to create UnfoldTransitionProgressProvider when the " - + "transition is disabled")); - - initUnfoldAnimationController(mUnfoldTransitionProgressProvider, - unfoldComponent.getRotationChangeProvider()); - } - /** Receives animation progress from sysui process. */ private void initRemotelyCalculatedUnfoldAnimation(UnfoldTransitionConfig config) { RemoteUnfoldSharedComponent unfoldComponent = @@ -980,6 +964,13 @@ public class QuickstepLauncher extends Launcher { return mSplitToWorkspaceController; } + @Override + protected void handleSplitAnimationGoingToHome() { + super.handleSplitAnimationGoingToHome(); + mSplitSelectStateController.getSplitAnimationController() + .playPlaceholderDismissAnim(this); + } + public <T extends OverviewActionsView> T getActionsView() { return (T) mActionsView; } @@ -1016,41 +1007,6 @@ public class QuickstepLauncher extends Launcher { } @Override - public DragOptions getDefaultWorkspaceDragOptions() { - if (mNextWorkspaceDragOptions != null) { - DragOptions options = mNextWorkspaceDragOptions; - mNextWorkspaceDragOptions = null; - return options; - } - return super.getDefaultWorkspaceDragOptions(); - } - - public void setNextWorkspaceDragOptions(DragOptions dragOptions) { - mNextWorkspaceDragOptions = dragOptions; - } - - @Override - public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { - QuickstepTransitionManager appTransitionManager = getAppTransitionManager(); - appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() { - @Override - public AnimatorSet createWindowAnimation(RemoteAnimationTarget[] appTargets, - RemoteAnimationTarget[] wallpaperTargets) { - - // On the first call clear the reference. - signal.cancel(); - - ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0); - fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets, - wallpaperTargets)); - AnimatorSet anim = new AnimatorSet(); - anim.play(fadeAnimation); - return anim; - } - }, signal); - } - - @Override public float[] getNormalOverviewScaleAndOffset() { return DisplayController.getNavigationMode(this).hasGestures ? new float[] {1, 1} : new float[] {1.1f, NO_OFFSET}; @@ -1100,6 +1056,8 @@ public class QuickstepLauncher extends Launcher { activityOptions.options.setLaunchDisplayId( (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId() : Display.DEFAULT_DISPLAY); + activityOptions.options.setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); addLaunchCookie(item, activityOptions.options); return activityOptions; } @@ -1112,6 +1070,8 @@ public class QuickstepLauncher extends Launcher { Executors.MAIN_EXECUTOR.getHandler(), null, elapsedRealTime -> callbacks.executeAllAndDestroy()); options.setSplashScreenStyle(splashScreenStyle); + options.setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); return new ActivityOptionsWrapper(options, callbacks); } @@ -1159,7 +1119,6 @@ public class QuickstepLauncher extends Launcher { } switch (info.itemType) { case Favorites.ITEM_TYPE_APPLICATION: - case Favorites.ITEM_TYPE_SHORTCUT: case Favorites.ITEM_TYPE_DEEP_SHORTCUT: case Favorites.ITEM_TYPE_APPWIDGET: // Fall through and continue if it's an app, shortcut, or widget @@ -1268,8 +1227,9 @@ public class QuickstepLauncher extends Launcher { Trace.instantForTrack(TRACE_TAG_APP, "QuickstepLauncher#DeviceProfileChanged", getDeviceProfile().toSmallString()); SystemUiProxy.INSTANCE.get(this).setLauncherAppIconSize(mDeviceProfile.iconSizePx); - if (mTaskbarManager != null) { - mTaskbarManager.debugWhyTaskbarNotDestroyed("QuickstepLauncher#onDeviceProfileChanged"); + TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager(); + if (taskbarManager != null) { + taskbarManager.debugWhyTaskbarNotDestroyed("QuickstepLauncher#onDeviceProfileChanged"); } } @@ -1291,7 +1251,7 @@ public class QuickstepLauncher extends Launcher { groupTask.task1.key.id, groupTask.task2.key.id, SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, - /* callback= */ success -> {}, + /* callback= */ success -> mSplitSelectStateController.resetState(), /* freezeTaskList= */ true, groupTask.mSplitBounds == null ? DEFAULT_SPLIT_RATIO @@ -1300,6 +1260,18 @@ public class QuickstepLauncher extends Launcher { : groupTask.mSplitBounds.leftTaskPercent); } + /** + * Launches two apps as an app pair. + */ + public void launchAppPair(WorkspaceItemInfo app1, WorkspaceItemInfo app2) { + mSplitSelectStateController.getAppPairsController().launchAppPair(app1, app2); + } + + public boolean canStartHomeSafely() { + OverviewCommandHelper overviewCommandHelper = mTISBindHelper.getOverviewCommandHelper(); + return overviewCommandHelper == null || overviewCommandHelper.canStartHomeSafely(); + } + private static final class LauncherTaskViewController extends TaskViewTouchController<Launcher> { @@ -1336,5 +1308,34 @@ public class QuickstepLauncher extends Launcher { if (recentsView != null) { recentsView.getSplitSelectController().dump(prefix, writer); } + if (mAppTransitionManager != null) { + mAppTransitionManager.dump(prefix + "\t" + RING_APPEAR_ANIMATION_PREFIX, writer); + } + if (mHotseatPredictionController != null) { + mHotseatPredictionController.dump(prefix, writer); + } + } + + @Override + public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { + switch (name) { + case "TextClock", "android.widget.TextClock" -> { + TextClock tc = new TextClock(context, attrs); + if (mAsyncClockEventDelegate == null) { + mAsyncClockEventDelegate = new AsyncClockEventDelegate(this); + } + tc.setClockEventDelegate(mAsyncClockEventDelegate); + return tc; + } + case "AnalogClock", "android.widget.AnalogClock" -> { + AnalogClock ac = new AnalogClock(context, attrs); + if (mAsyncClockEventDelegate == null) { + mAsyncClockEventDelegate = new AsyncClockEventDelegate(this); + } + ac.setClockEventDelegate(mAsyncClockEventDelegate); + return ac; + } + } + return super.onCreateView(parent, name, context, attrs); } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java index 39543b0d7d..f7bef031c7 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java @@ -243,6 +243,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder { } else { widgetView = new LauncherAppWidgetHostView(context); } + widgetView.setIsWidgetCachingDisabled(true); widgetView.setInteractionHandler(mInteractionHandler); widgetView.setAppWidget(appWidgetId, appWidget); mViews.put(appWidgetId, widgetView); diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java index f16b43df5e..23e922c945 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java @@ -15,10 +15,10 @@ */ package com.android.launcher3.uioverrides; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON; import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS; import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE; import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE; import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA; diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java index b901a87753..a76eb437de 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java +++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java @@ -23,6 +23,7 @@ import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS; import static android.view.View.GONE; import static android.view.View.VISIBLE; +import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD; import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY; import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED; import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey; @@ -59,6 +60,7 @@ import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceViewHolder; +import androidx.preference.SeekBarPreference; import androidx.preference.SwitchPreference; import com.android.launcher3.LauncherPrefs; @@ -106,6 +108,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat { loadPluginPrefs(); maybeAddSandboxCategory(); addOnboardingPrefsCatergory(); + if (FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()) { + addAllAppsFromOverviewCatergory(); + } if (getActivity() != null) { getActivity().setTitle("Developer Options"); @@ -393,6 +398,33 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat { } } + private void addAllAppsFromOverviewCatergory() { + PreferenceCategory category = newCategory("All Apps from Overview Config"); + + SeekBarPreference thresholdPref = new SeekBarPreference(getContext()); + thresholdPref.setTitle("Threshold to open All Apps from Overview"); + thresholdPref.setSingleLineTitle(false); + + // These values are 100x swipe up shift value (100 = where overview sits). + thresholdPref.setMax(500); + thresholdPref.setMin(105); + thresholdPref.setUpdatesContinuously(true); + thresholdPref.setIconSpaceReserved(false); + // Don't directly save to shared prefs, use LauncherPrefs instead. + thresholdPref.setPersistent(false); + thresholdPref.setOnPreferenceChangeListener((preference, newValue) -> { + LauncherPrefs.get(getContext()).put(ALL_APPS_OVERVIEW_THRESHOLD, newValue); + preference.setSummary(String.valueOf((int) newValue / 100f)); + return true; + }); + int value = LauncherPrefs.get(getContext()).get(ALL_APPS_OVERVIEW_THRESHOLD); + thresholdPref.setValue(value); + // For some reason the initial value is not triggering the summary update, so call manually. + thresholdPref.getOnPreferenceChangeListener().onPreferenceChange(thresholdPref, value); + + category.addPreference(thresholdPref); + } + private String toName(String action) { String str = action.replace("com.android.systemui.action.PLUGIN_", "") .replace("com.android.launcher3.action.PLUGIN_", ""); diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java index a68e753589..6279f634d6 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java @@ -116,8 +116,9 @@ public class FlagsFactory { boolean defaultValue = DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, key, defaultValueInCode); if (IS_DEBUG_DEVICE) { boolean currentValue = getSharedPreferences().getBoolean(key, defaultValue); - DebugFlag flag = new DeviceFlag(key, description, flagState, currentValue, - defaultValueInCode); + DebugFlag flag = new DeviceFlag(key, description, + (defaultValue == defaultValueInCode) ? flagState + : defaultValue ? ENABLED : DISABLED, currentValue, defaultValueInCode); sDebugFlags.add(flag); return flag; } else { diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java index 2a42175b5b..ed0a0d5172 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java @@ -15,14 +15,16 @@ */ package com.android.launcher3.uioverrides.states; -import static com.android.launcher3.anim.Interpolators.DEACCEL_2; +import static com.android.app.animation.Interpolators.DECELERATE_2; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS; import android.content.Context; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.R; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ActivityContext; @@ -91,7 +93,7 @@ public class AllAppsState extends LauncherState { @Override public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) { PageAlphaProvider superPageAlphaProvider = super.getWorkspacePageAlphaProvider(launcher); - return new PageAlphaProvider(DEACCEL_2) { + return new PageAlphaProvider(DECELERATE_2) { @Override public float getPageAlpha(int pageIndex) { return launcher.getDeviceProfile().isTablet @@ -103,14 +105,52 @@ public class AllAppsState extends LauncherState { @Override public int getVisibleElements(Launcher launcher) { - // Don't add HOTSEAT_ICONS for non-tablets in ALL_APPS state. - return launcher.getDeviceProfile().isTablet ? ALL_APPS_CONTENT | HOTSEAT_ICONS - : ALL_APPS_CONTENT; + int elements = ALL_APPS_CONTENT | FLOATING_SEARCH_BAR; + // Only add HOTSEAT_ICONS for tablets in ALL_APPS state. + if (launcher.getDeviceProfile().isTablet) { + elements |= HOTSEAT_ICONS; + } + return elements; + } + + @Override + public int getFloatingSearchBarRestingMarginBottom(Launcher launcher) { + return 0; + } + + @Override + public int getFloatingSearchBarRestingMarginStart(Launcher launcher) { + DeviceProfile dp = launcher.getDeviceProfile(); + return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin(); + } + + @Override + public int getFloatingSearchBarRestingMarginEnd(Launcher launcher) { + DeviceProfile dp = launcher.getDeviceProfile(); + return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin(); + } + + @Override + public boolean shouldFloatingSearchBarUsePillWhenUnfocused(Launcher launcher) { + DeviceProfile dp = launcher.getDeviceProfile(); + return dp.isPhone && !dp.isLandscape; } @Override public LauncherState getHistoryForState(LauncherState previousState) { - return previousState == OVERVIEW ? OVERVIEW : NORMAL; + return previousState == BACKGROUND_APP ? QUICK_SWITCH_FROM_HOME + : previousState == OVERVIEW ? OVERVIEW : NORMAL; + } + + @Override + public float[] getOverviewScaleAndOffset(Launcher launcher) { + if (!FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()) { + return super.getOverviewScaleAndOffset(launcher); + } + // This handles the case of returning to the previous app from Overview -> All Apps gesture. + // This is the start scale/offset of overview that will be used for that transition. + // TODO (b/283336332): Translate in Y direction (ideally with overview resistance). + return new float[] {0.5f /* scale */, NO_OFFSET}; } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java index 214679acbe..396d0abeb0 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -15,7 +15,7 @@ */ package com.android.launcher3.uioverrides.states; -import static com.android.launcher3.anim.Interpolators.DEACCEL_2; +import static com.android.app.animation.Interpolators.DECELERATE_2; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW; import android.content.Context; @@ -97,7 +97,7 @@ public class OverviewState extends LauncherState { @Override public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) { - return new PageAlphaProvider(DEACCEL_2) { + return new PageAlphaProvider(DECELERATE_2) { @Override public float getPageAlpha(int pageIndex) { return 0; @@ -107,7 +107,32 @@ public class OverviewState extends LauncherState { @Override public int getVisibleElements(Launcher launcher) { - return CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS; + int elements = CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS; + DeviceProfile dp = launcher.getDeviceProfile(); + boolean showFloatingSearch; + if (dp.isPhone) { + // Only show search in phone overview in portrait mode. + showFloatingSearch = !dp.isLandscape; + } else { + // Only show search in tablet overview if taskbar is not visible. + showFloatingSearch = !dp.isTaskbarPresent || isTaskbarStashed(launcher); + } + if (showFloatingSearch) { + elements |= FLOATING_SEARCH_BAR; + } + return elements; + } + + @Override + public int getFloatingSearchBarRestingMarginBottom(Launcher launcher) { + return areElementsVisible(launcher, FLOATING_SEARCH_BAR) ? 0 + : super.getFloatingSearchBarRestingMarginBottom(launcher); + } + + @Override + public boolean shouldFloatingSearchBarUsePillWhenUnfocused(Launcher launcher) { + DeviceProfile dp = launcher.getDeviceProfile(); + return dp.isPhone && !dp.isLandscape; } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java index a8d7538687..6651c7399e 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java @@ -17,6 +17,20 @@ package com.android.launcher3.uioverrides.states; import static android.view.View.VISIBLE; +import static com.android.app.animation.Interpolators.ACCELERATE; +import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE; +import static com.android.app.animation.Interpolators.DECELERATE; +import static com.android.app.animation.Interpolators.DECELERATE_1_7; +import static com.android.app.animation.Interpolators.DECELERATE_3; +import static com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE; +import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE; +import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.app.animation.Interpolators.FINAL_FRAME; +import static com.android.app.animation.Interpolators.INSTANT; +import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.OVERSHOOT_0_75; +import static com.android.app.animation.Interpolators.OVERSHOOT_1_2; +import static com.android.app.animation.Interpolators.clampToProgress; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON; @@ -25,20 +39,6 @@ import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; import static com.android.launcher3.QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION; import static com.android.launcher3.WorkspaceStateTransitionAnimation.getWorkspaceSpringScaleAnimator; -import static com.android.launcher3.anim.Interpolators.ACCEL; -import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; -import static com.android.launcher3.anim.Interpolators.DEACCEL; -import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7; -import static com.android.launcher3.anim.Interpolators.DEACCEL_3; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; -import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; -import static com.android.launcher3.anim.Interpolators.INSTANT; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.OVERSHOOT_0_75; -import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; -import static com.android.launcher3.anim.Interpolators.clampToProgress; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE; @@ -55,12 +55,14 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_T import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import android.animation.ValueAnimator; +import android.util.Log; import com.android.launcher3.CellLayout; import com.android.launcher3.Hotseat; import com.android.launcher3.LauncherState; import com.android.launcher3.Workspace; import com.android.launcher3.states.StateAnimationConfig; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.touch.AllAppsSwipeController; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.DisplayController; @@ -94,7 +96,8 @@ public class QuickstepAtomicAnimationFactory extends @Override public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState, StateAnimationConfig config) { - + Log.d(TestProtocol.OVERVIEW_OVER_HOME, "creating animation fromState: " + + fromState + " toState: " + toState); RecentsView overview = mActivity.getOverviewPanel(); if ((fromState == OVERVIEW || fromState == OVERVIEW_SPLIT_SELECT) && toState == NORMAL) { overview.switchToScreenshot(() -> @@ -112,8 +115,8 @@ public class QuickstepAtomicAnimationFactory extends fromState == OVERVIEW_SPLIT_SELECT ? clampToProgress(LINEAR, 0.33f, 1) : LINEAR); - config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL); - config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL); + config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATE); + config.setInterpolator(ANIM_WORKSPACE_FADE, ACCELERATE); if (DisplayController.getNavigationMode(mActivity).hasGestures && overview.getTaskViewCount() > 0) { @@ -139,9 +142,9 @@ public class QuickstepAtomicAnimationFactory extends } overview.snapToPage(DEFAULT_PAGE, Math.toIntExact(config.duration)); } else { - config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL); - config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f)); - config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7); + config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCELERATE_DECELERATE); + config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCELERATE, 0, 0.9f)); + config.setInterpolator(ANIM_OVERVIEW_FADE, DECELERATE_1_7); } Workspace<?> workspace = mActivity.getWorkspace(); @@ -167,8 +170,8 @@ public class QuickstepAtomicAnimationFactory extends || fromState == HINT_STATE_TWO_BUTTON) && toState == OVERVIEW) { if (DisplayController.getNavigationMode(mActivity).hasGestures) { config.setInterpolator(ANIM_WORKSPACE_SCALE, - fromState == NORMAL ? ACCEL : OVERSHOOT_1_2); - config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL); + fromState == NORMAL ? ACCELERATE : OVERSHOOT_1_2); + config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCELERATE); // Scrolling in tasks, so show straight away if (overview.getTaskViewCount() > 0) { @@ -196,7 +199,7 @@ public class QuickstepAtomicAnimationFactory extends config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_2); config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_2); } else if (fromState == HINT_STATE && toState == NORMAL) { - config.setInterpolator(ANIM_DEPTH, DEACCEL_3); + config.setInterpolator(ANIM_DEPTH, DECELERATE_3); if (mHintToNormalDuration == -1) { ValueAnimator va = getWorkspaceSpringScaleAnimator(mActivity, mActivity.getWorkspace(), diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java index 8cbd6e8b47..b266bcd246 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -15,19 +15,22 @@ */ package com.android.launcher3.uioverrides.touchcontrollers; +import static com.android.app.animation.Interpolators.DECELERATE_3; import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU; import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.LauncherAnimUtils.newCancelListener; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent; import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PULL_BACK_ALPHA; import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PULL_BACK_TRANSLATION; import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback; -import static com.android.launcher3.anim.Interpolators.DEACCEL_3; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE; +import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; +import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.view.MotionEvent; import android.view.animation.Interpolator; @@ -41,24 +44,29 @@ import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.compat.AccessibilityManagerCompat; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.touch.SingleAxisSwipeDetector; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.TouchController; import com.android.quickstep.TaskUtils; import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.OverviewToHomeAnim; import com.android.quickstep.views.RecentsView; +import java.util.function.Consumer; + /** * Handles swiping up on the nav bar to go home from launcher, e.g. overview or all apps. */ public class NavBarToHomeTouchController implements TouchController, SingleAxisSwipeDetector.Listener { - private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3; + private static final Interpolator PULLBACK_INTERPOLATOR = DECELERATE_3; // The min amount of overview scrim we keep during the transition. private static final float OVERVIEW_TO_HOME_SCRIM_MULTIPLIER = 0.5f; private final Launcher mLauncher; + private final Consumer<AnimatorSet> mCancelSplitRunnable; private final SingleAxisSwipeDetector mSwipeDetector; private final float mPullbackDistance; @@ -67,8 +75,14 @@ public class NavBarToHomeTouchController implements TouchController, private LauncherState mEndState = NORMAL; private AnimatorPlaybackController mCurrentAnimation; - public NavBarToHomeTouchController(Launcher launcher) { + /** + * @param cancelSplitRunnable Called when split placeholder view needs to be cancelled. + * Animation should be added to the provided AnimatorSet + */ + public NavBarToHomeTouchController(Launcher launcher, + Consumer<AnimatorSet> cancelSplitRunnable) { mLauncher = launcher; + mCancelSplitRunnable = cancelSplitRunnable; mSwipeDetector = new SingleAxisSwipeDetector(mLauncher, this, SingleAxisSwipeDetector.VERTICAL); mPullbackDistance = mLauncher.getResources().getDimension(R.dimen.home_pullback_distance); @@ -95,6 +109,10 @@ public class NavBarToHomeTouchController implements TouchController, } private boolean canInterceptTouch(MotionEvent ev) { + if (!isTrackpadMotionEvent(ev) && DisplayController.getNavigationMode(mLauncher) + == THREE_BUTTONS) { + return false; + } boolean cameFromNavBar = (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0; if (!cameFromNavBar) { return false; @@ -176,7 +194,10 @@ public class NavBarToHomeTouchController implements TouchController, recentsView.switchToScreenshot(null, () -> recentsView.finishRecentsAnimation(true /* toRecents */, null)); if (mStartState.overviewUi) { - new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(mEndState)) + new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(mEndState), + FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get() + ? mCancelSplitRunnable + : null) .animateWithVelocity(velocity); } else { mLauncher.getStateManager().goToState(mEndState, true, diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java index b7bafd8969..ca598c8411 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java @@ -16,19 +16,22 @@ package com.android.launcher3.uioverrides.touchcontrollers; +import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE; import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR; import static com.android.launcher3.LauncherAnimUtils.newCancelListener; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback; -import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; +import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; +import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.graphics.PointF; @@ -43,6 +46,7 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.taskbar.LauncherTaskbarUIController; import com.android.launcher3.uioverrides.QuickstepLauncher; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.VibratorWrapper; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.util.AnimatorControllerWithResistance; @@ -50,6 +54,8 @@ import com.android.quickstep.util.MotionPauseDetector; import com.android.quickstep.util.OverviewToHomeAnim; import com.android.quickstep.views.RecentsView; +import java.util.function.Consumer; + /** * Touch controller which handles swipe and hold from the nav bar to go to Overview. Swiping above * the nav bar falls back to go to All Apps. Swiping from the nav bar without holding goes to the @@ -64,6 +70,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch private static final float TRANSLATION_ANIM_VELOCITY_DP_PER_MS = 0.8f; private final VibratorWrapper mVibratorWrapper; + private final Consumer<AnimatorSet> mCancelSplitRunnable; private final RecentsView mRecentsView; private final MotionPauseDetector mMotionPauseDetector; private final float mMotionPauseMinDisplacement; @@ -79,16 +86,26 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch // Normal to Hint animation has flag SKIP_OVERVIEW, so we update this scrim with this animator. private ObjectAnimator mNormalToHintOverviewScrimAnimator; - public NoButtonNavbarToOverviewTouchController(Launcher l) { + /** + * @param cancelSplitRunnable Called when split placeholder view needs to be cancelled. + * Animation should be added to the provided AnimatorSet + */ + public NoButtonNavbarToOverviewTouchController(Launcher l, + Consumer<AnimatorSet> cancelSplitRunnable) { super(l); mRecentsView = l.getOverviewPanel(); mMotionPauseDetector = new MotionPauseDetector(l); mMotionPauseMinDisplacement = ViewConfiguration.get(l).getScaledTouchSlop(); mVibratorWrapper = VibratorWrapper.INSTANCE.get(l.getApplicationContext()); + mCancelSplitRunnable = cancelSplitRunnable; } @Override protected boolean canInterceptTouch(MotionEvent ev) { + if (!isTrackpadMotionEvent(ev) && DisplayController.getNavigationMode(mLauncher) + == THREE_BUTTONS) { + return false; + } mDidTouchStartInNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0; boolean isOneHandedModeActive = (SystemUiProxy.INSTANCE.get(mLauncher) .getLastSystemUiStateFlags() & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0; @@ -102,9 +119,6 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) { if (fromState == NORMAL && mDidTouchStartInNavBar) { return HINT_STATE; - } else if (fromState == OVERVIEW && isDragTowardPositive) { - // Don't allow swiping up to all apps. - return OVERVIEW; } return super.getTargetState(fromState, isDragTowardPositive); } @@ -190,6 +204,9 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch // state, but since the hint state tracks the entire screen without a clear endpoint, we // need to manually set the duration to a reasonable value. animator.setDuration(HINT_STATE.getTransitionDuration(mLauncher, true /* isToState */)); + AnimatorSet animatorSet = new AnimatorSet(); + mCancelSplitRunnable.accept(animatorSet); + animatorSet.start(); } if (FeatureFlags.ENABLE_PREMIUM_HAPTICS_ALL_APPS.get() && ((mFromState == NORMAL && mToState == ALL_APPS) @@ -261,7 +278,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch private void goToOverviewOrHomeOnDragEnd(float velocity) { boolean goToHomeInsteadOfOverview = !mMotionPauseDetector.isPaused(); if (goToHomeInsteadOfOverview) { - new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(NORMAL)) + new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(NORMAL), null) .animateWithVelocity(velocity); } if (mReachedOverview) { @@ -273,7 +290,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch mRecentsView.animate() .translationX(0) .translationY(0) - .setInterpolator(ACCEL_DEACCEL) + .setInterpolator(ACCELERATE_DECELERATE) .setDuration(duration) .withEndAction(goToHomeInsteadOfOverview ? null diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java index 80f5558315..6f421eb14a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java @@ -16,21 +16,21 @@ package com.android.launcher3.uioverrides.touchcontrollers; import static android.view.MotionEvent.ACTION_DOWN; -import static android.view.MotionEvent.ACTION_MOVE; +import static com.android.app.animation.Interpolators.ACCELERATE_0_75; +import static com.android.app.animation.Interpolators.DECELERATE_3; +import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.LauncherAnimUtils.newCancelListener; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS; import static com.android.launcher3.LauncherState.QUICK_SWITCH_FROM_HOME; import static com.android.launcher3.MotionEventsUtils.isTrackpadFourFingerSwipe; +import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent; import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe; import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD; import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; -import static com.android.launcher3.anim.Interpolators.ACCEL_0_75; -import static com.android.launcher3.anim.Interpolators.DEACCEL_3; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN; @@ -46,6 +46,7 @@ import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM; import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT; import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP; +import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET; @@ -74,6 +75,7 @@ import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.touch.BaseSwipeDetector; import com.android.launcher3.touch.BothAxesSwipeDetector; import com.android.launcher3.uioverrides.QuickstepLauncher; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.TouchController; import com.android.launcher3.util.VibratorWrapper; import com.android.quickstep.SystemUiProxy; @@ -84,6 +86,7 @@ import com.android.quickstep.util.WorkspaceRevealAnim; import com.android.quickstep.views.DesktopTaskView; import com.android.quickstep.views.LauncherRecentsView; import com.android.quickstep.views.RecentsView; +import com.android.systemui.shared.system.InteractionJankMonitorWrapper; /** * Handles quick switching to a recent task from the home screen. To give as much flexibility to @@ -93,8 +96,8 @@ public class NoButtonQuickSwitchTouchController implements TouchController, BothAxesSwipeDetector.Listener { private static final float Y_ANIM_MIN_PROGRESS = 0.25f; - private static final Interpolator FADE_OUT_INTERPOLATOR = DEACCEL_3; - private static final Interpolator TRANSLATE_OUT_INTERPOLATOR = ACCEL_0_75; + private static final Interpolator FADE_OUT_INTERPOLATOR = DECELERATE_3; + private static final Interpolator TRANSLATE_OUT_INTERPOLATOR = ACCELERATE_0_75; private static final Interpolator SCALE_DOWN_INTERPOLATOR = LINEAR; private static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300; @@ -110,7 +113,6 @@ public class NoButtonQuickSwitchTouchController implements TouchController, newCancelListener(this::clearState); private boolean mNoIntercept; - private Boolean mIsTrackpadFourFingerSwipe; private LauncherState mStartState; private boolean mIsHomeScreenVisible = true; @@ -136,9 +138,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { - int action = ev.getActionMasked(); - if (action == ACTION_DOWN) { - mIsTrackpadFourFingerSwipe = null; + if (ev.getActionMasked() == ACTION_DOWN) { mNoIntercept = !canInterceptTouch(ev); if (mNoIntercept) { return false; @@ -147,13 +147,6 @@ public class NoButtonQuickSwitchTouchController implements TouchController, // Only detect horizontal swipe for intercept, then we will allow swipe up as well. mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT, false /* ignoreSlopWhenSettling */); - } else if (isTrackpadMultiFingerSwipe(ev) && mIsTrackpadFourFingerSwipe == null - && action == ACTION_MOVE) { - mIsTrackpadFourFingerSwipe = isTrackpadFourFingerSwipe(ev); - mNoIntercept = !mIsTrackpadFourFingerSwipe; - if (mNoIntercept) { - return false; - } } if (mNoIntercept) { @@ -170,6 +163,10 @@ public class NoButtonQuickSwitchTouchController implements TouchController, } private boolean canInterceptTouch(MotionEvent ev) { + if (!isTrackpadMotionEvent(ev) && DisplayController.getNavigationMode(mLauncher) + == THREE_BUTTONS) { + return false; + } if (!mLauncher.isInState(LauncherState.NORMAL)) { return false; } @@ -184,6 +181,9 @@ public class NoButtonQuickSwitchTouchController implements TouchController, // TODO(b/268075592): add support for quickswitch to/from desktop return false; } + if (isTrackpadMultiFingerSwipe(ev)) { + return isTrackpadFourFingerSwipe(ev); + } return true; } @@ -191,6 +191,9 @@ public class NoButtonQuickSwitchTouchController implements TouchController, public void onDragStart(boolean start) { mMotionPauseDetector.clear(); if (start) { + InteractionJankMonitorWrapper.begin(mRecentsView, + InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH); + mStartState = mLauncher.getStateManager().getState(); mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected); @@ -325,6 +328,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, if (mMotionPauseDetector.isPaused() && noFling) { // Going to Overview. cancelAnimations(); + InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH); StateAnimationConfig config = new StateAnimationConfig(); config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW; @@ -441,6 +445,8 @@ public class NoButtonQuickSwitchTouchController implements TouchController, RecentsView.SCROLL_VIBRATION_PRIMITIVE, RecentsView.SCROLL_VIBRATION_PRIMITIVE_SCALE, RecentsView.SCROLL_VIBRATION_FALLBACK); + } else { + InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH); } nonOverviewAnim.setDuration(Math.max(xDuration, yDuration)); @@ -462,6 +468,11 @@ public class NoButtonQuickSwitchTouchController implements TouchController, : targetState.ordinal > mStartState.ordinal ? LAUNCHER_UNKNOWN_SWIPEUP : LAUNCHER_UNKNOWN_SWIPEDOWN)); + + if (targetState == QUICK_SWITCH_FROM_HOME) { + InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH); + } + mLauncher.getStateManager().goToState(targetState, false, forEndCallback(this::clearState)); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java index 8368f9ca03..e30fe667ff 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java @@ -24,11 +24,12 @@ import static com.android.launcher3.LauncherState.OVERVIEW; import android.view.MotionEvent; +import com.android.app.animation.Interpolators; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.allapps.AllAppsTransitionController; -import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.touch.AbstractStateChangeTouchController; import com.android.launcher3.touch.AllAppsSwipeController; @@ -92,9 +93,9 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr @Override protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) { if (fromState == ALL_APPS && !isDragTowardPositive) { - return NORMAL; - } else if (fromState == OVERVIEW) { - return isDragTowardPositive ? OVERVIEW : NORMAL; + return FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get() + ? mLauncher.getStateManager().getLastState() + : NORMAL; } else if (fromState == NORMAL && isDragTowardPositive) { return ALL_APPS; } diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java index f941b02065..9a35bb2ae7 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java @@ -15,12 +15,12 @@ */ package com.android.launcher3.uioverrides.touchcontrollers; +import static com.android.app.animation.Interpolators.ACCELERATE_2; +import static com.android.app.animation.Interpolators.DECELERATE_2; +import static com.android.app.animation.Interpolators.INSTANT; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.QUICK_SWITCH_FROM_HOME; -import static com.android.launcher3.anim.Interpolators.ACCEL_2; -import static com.android.launcher3.anim.Interpolators.DEACCEL_2; -import static com.android.launcher3.anim.Interpolators.INSTANT; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE; @@ -128,14 +128,14 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll } private void setupInterpolators(StateAnimationConfig stateAnimationConfig) { - stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_2); - stateAnimationConfig.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_2); + stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_FADE, DECELERATE_2); + stateAnimationConfig.setInterpolator(ANIM_ALL_APPS_FADE, DECELERATE_2); if (DisplayController.getNavigationMode(mLauncher) == NavigationMode.NO_BUTTON) { // Overview lives to the left of workspace, so translate down later than over - stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL_2); - stateAnimationConfig.setInterpolator(ANIM_VERTICAL_PROGRESS, ACCEL_2); - stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_SCALE, ACCEL_2); - stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, ACCEL_2); + stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCELERATE_2); + stateAnimationConfig.setInterpolator(ANIM_VERTICAL_PROGRESS, ACCELERATE_2); + stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_SCALE, ACCELERATE_2); + stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, ACCELERATE_2); stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT); } else { stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_TRANSLATE, LINEAR); diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java index 395833ffea..26ab3d6c52 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java @@ -76,7 +76,7 @@ public class StatusBarTouchController implements TouchController { private void dispatchTouchEvent(MotionEvent ev) { if (mSystemUiProxy.isActive()) { mLastAction = ev.getActionMasked(); - mSystemUiProxy.onStatusBarMotionEvent(ev); + mSystemUiProxy.onStatusBarTouchEvent(ev); } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java index eddc50c64f..3d94857848 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java @@ -27,13 +27,13 @@ import android.view.MotionEvent; import android.view.View; import android.view.animation.Interpolator; +import com.android.app.animation.Interpolators; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.touch.BaseSwipeDetector; import com.android.launcher3.touch.PagedOrientationHandler; diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 43db7c380c..1ef4039ce9 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -21,19 +21,19 @@ import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.widget.Toast.LENGTH_SHORT; +import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE; +import static com.android.app.animation.Interpolators.DECELERATE; +import static com.android.app.animation.Interpolators.OVERSHOOT_1_2; import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; +import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD; import static com.android.launcher3.PagedView.INVALID_PAGE; -import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; -import static com.android.launcher3.anim.Interpolators.DEACCEL; -import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT; -import static com.android.launcher3.uioverrides.QuickstepLauncher.ENABLE_PIP_KEEP_CLEAR_ALGORITHM; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK; @@ -94,6 +94,7 @@ import androidx.annotation.UiThread; import com.android.internal.util.LatencyTracker; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherPrefs; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; @@ -101,14 +102,15 @@ import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.StatsLogger; +import com.android.launcher3.statehandlers.DesktopVisibilityController; import com.android.launcher3.statemanager.BaseState; import com.android.launcher3.statemanager.StatefulActivity; +import com.android.launcher3.taskbar.TaskbarThresholdUtils; import com.android.launcher3.taskbar.TaskbarUIController; -import com.android.launcher3.tracing.InputConsumerProto; -import com.android.launcher3.tracing.SwipeHandlerProto; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter; import com.android.launcher3.util.DisplayController; +import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.TraceHelper; import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.util.WindowBounds; @@ -122,7 +124,6 @@ import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.InputConsumerProxy; import com.android.quickstep.util.InputProxyHandlerFactory; import com.android.quickstep.util.MotionPauseDetector; -import com.android.quickstep.util.ProtoTracer; import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.StaggeredWorkspaceAnim; @@ -133,6 +134,7 @@ import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.views.DesktopTaskView; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; +import com.android.quickstep.views.TaskView.TaskIdAttributeContainer; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -149,6 +151,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Objects; import java.util.Optional; +import java.util.OptionalInt; import java.util.function.Consumer; /** @@ -163,9 +166,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, private static final ArrayList<String> STATE_NAMES = new ArrayList<>(); - /** Shift distance to transition to All Apps if ENABLE_ALL_APPS_FROM_OVERVIEW. */ - public static final float ALL_APPS_SHIFT_THRESHOLD = 2f; - protected final BaseActivityInterface<S, T> mActivityInterface; protected final InputConsumerProxy mInputConsumerProxy; protected final ActivityInitListener mActivityInitListener; @@ -193,6 +193,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, ActiveGestureLog.INSTANCE.addLog("Launcher destroyed", LAUNCHER_DESTROYED); mRecentsView = null; mActivity = null; + mStateCallback.clearState(STATE_LAUNCHER_PRESENT); } }; @@ -293,7 +294,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, private boolean mContinuingLastGesture; - private ThumbnailData mTaskSnapshot; + // Cache of recently-updated task snapshots, mapping task id to ThumbnailData + private HashMap<Integer, ThumbnailData> mTaskSnapshotCache = new HashMap<>(); // Used to control launcher components throughout the swipe gesture. private AnimatorControllerWithResistance mLauncherTransitionController; @@ -378,12 +380,12 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, mTaskbarAlreadyOpen = controller != null && !controller.isTaskbarStashed(); mIsTaskbarAllAppsOpen = controller != null && controller.isTaskbarAllAppsOpen(); mTaskbarAppWindowThreshold = - res.getDimensionPixelSize(R.dimen.taskbar_app_window_threshold); + TaskbarThresholdUtils.getAppWindowThreshold(res, mDp); boolean swipeWillNotShowTaskbar = mTaskbarAlreadyOpen || mGestureState.isTrackpadGesture(); mTaskbarHomeOverviewThreshold = swipeWillNotShowTaskbar ? 0 - : res.getDimensionPixelSize(R.dimen.taskbar_home_overview_threshold); - mTaskbarCatchUpThreshold = res.getDimensionPixelSize(R.dimen.taskbar_catch_up_threshold); + : TaskbarThresholdUtils.getHomeOverviewThreshold(res, mDp); + mTaskbarCatchUpThreshold = TaskbarThresholdUtils.getCatchUpThreshold(res, mDp); } @Nullable @@ -516,20 +518,22 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, // Set up a entire animation lifecycle callback to notify the current recents view when // the animation is canceled mGestureState.runOnceAtState(STATE_RECENTS_ANIMATION_CANCELED, () -> { - HashMap<Integer, ThumbnailData> snapshots = - mGestureState.consumeRecentsAnimationCanceledSnapshot(); - if (snapshots != null) { - mRecentsView.switchToScreenshot(snapshots, () -> { - if (mRecentsAnimationController != null) { - mRecentsAnimationController.cleanupScreenshot(); - } else if (mDeferredCleanupRecentsAnimationController != null) { - mDeferredCleanupRecentsAnimationController.cleanupScreenshot(); - mDeferredCleanupRecentsAnimationController = null; - } - }); - mRecentsView.onRecentsAnimationComplete(); - } - }); + if (mRecentsView == null) return; + + HashMap<Integer, ThumbnailData> snapshots = + mGestureState.consumeRecentsAnimationCanceledSnapshot(); + if (snapshots != null) { + mRecentsView.switchToScreenshot(snapshots, () -> { + if (mRecentsAnimationController != null) { + mRecentsAnimationController.cleanupScreenshot(); + } else if (mDeferredCleanupRecentsAnimationController != null) { + mDeferredCleanupRecentsAnimationController.cleanupScreenshot(); + mDeferredCleanupRecentsAnimationController = null; + } + }); + mRecentsView.onRecentsAnimationComplete(); + } + }); setupRecentsViewUi(); mRecentsView.runOnPageScrollsInitialized(this::linkRecentsViewScroll); @@ -587,7 +591,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, if (mWasLauncherAlreadyVisible) { mStateCallback.setState(STATE_LAUNCHER_DRAWN); } else { - Object traceToken = TraceHelper.INSTANCE.beginSection("WTS-init"); + SafeCloseable traceToken = TraceHelper.INSTANCE.beginAsyncSection("WTS-init"); View dragLayer = activity.getDragLayer(); dragLayer.getViewTreeObserver().addOnDrawListener(new OnDrawListener() { boolean mHandled = false; @@ -599,7 +603,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } mHandled = true; - TraceHelper.INSTANCE.endSection(traceToken); + traceToken.close(); dragLayer.post(() -> dragLayer.getViewTreeObserver().removeOnDrawListener(this)); if (activity != mActivity) { @@ -657,11 +661,12 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, protected void notifyGestureAnimationStartToRecents() { Task[] runningTasks; + TopTaskTracker.CachedTaskInfo cachedTaskInfo = mGestureState.getRunningTask(); if (mIsSwipeForSplit) { int[] splitTaskIds = TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds(); - runningTasks = mGestureState.getRunningTask().getPlaceholderTasks(splitTaskIds); + runningTasks = cachedTaskInfo.getPlaceholderTasks(splitTaskIds); } else { - runningTasks = mGestureState.getRunningTask().getPlaceholderTasks(); + runningTasks = cachedTaskInfo.getPlaceholderTasks(); } // Safeguard against any null tasks being sent to recents view, happens when quickswitching @@ -681,11 +686,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, private void initializeLauncherAnimationController() { buildAnimationController(); - Object traceToken = TraceHelper.INSTANCE.beginSection("logToggleRecents", - TraceHelper.FLAG_IGNORE_BINDERS); - LatencyTracker.getInstance(mContext).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS, - (int) (mLauncherFrameDrawnTime - mTouchTimeMs)); - TraceHelper.INSTANCE.endSection(traceToken); + try (SafeCloseable c = TraceHelper.INSTANCE.allowIpcs("logToggleRecents")) { + LatencyTracker.getInstance(mContext).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS, + (int) (mLauncherFrameDrawnTime - mTouchTimeMs)); + } // This method is only called when STATE_GESTURE_STARTED is set, so we can enable the // high-res thumbnail loader here once we are sure that we will end up in an overview state @@ -731,11 +735,15 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, * @param moveRunningTask whether to move running task to front when attaching */ private void maybeUpdateRecentsAttachedState(boolean animate, boolean moveRunningTask) { - if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) { + if ((!mDeviceState.isFullyGesturalNavMode() && !mGestureState.isTrackpadGesture()) + || mRecentsView == null) { return; } + // looking at single target is fine here since either app of a split pair would + // have their "isInRecents" field set? (that's what this is used for below) RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null - ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId()) + ? mRecentsAnimationTargets + .findTask(mGestureState.getTopRunningTaskId()) : null; final boolean recentsAttachedToAppWindow; if (mIsInAllAppsRegion) { @@ -810,6 +818,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC); maybeUpdateRecentsAttachedState(true); + if (mActivity != null) { + mActivity.getAppsView().getSearchUiManager().prepareToFocusEditText(mIsInAllAppsRegion); + } + // Draw active task below Launcher so that All Apps can appear over it. runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(isInAllAppsRegion)); @@ -873,7 +885,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, @UiThread @Override public void onCurrentShiftUpdated() { - setIsInAllAppsRegion(mCurrentShift.value >= ALL_APPS_SHIFT_THRESHOLD); + float threshold = LauncherPrefs.get(mContext).get(ALL_APPS_OVERVIEW_THRESHOLD) / 100f; + setIsInAllAppsRegion(mCurrentShift.value >= threshold); updateSysUiFlags(mCurrentShift.value); applyScrollAndTransform(); @@ -1148,6 +1161,14 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT); // Notify the SysUI to use fade-in animation when entering PiP SystemUiProxy.INSTANCE.get(mContext).setPipAnimationTypeToAlpha(); + if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) { + // Notify the SysUI to stash desktop apps if they are visible + DesktopVisibilityController desktopVisibilityController = + mActivityInterface.getDesktopVisibilityController(); + if (desktopVisibilityController != null) { + desktopVisibilityController.onHomeActionTriggered(); + } + } break; case RECENTS: mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT @@ -1178,7 +1199,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, return false; } boolean hasStartedTaskBefore = Arrays.stream(appearedTaskTarget).anyMatch( - targetCompat -> targetCompat.taskId == mGestureState.getLastStartedTaskId()); + mGestureState.mLastStartedTaskIdPredicate); if (mStateCallback.hasStates(STATE_START_NEW_TASK) && hasStartedTaskBefore) { reset(); return true; @@ -1324,11 +1345,11 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, Interpolator interpolator; S state = mActivityInterface.stateFromGestureEndTarget(endTarget); if (state.displayOverviewTasksAsGrid(mDp)) { - interpolator = ACCEL_DEACCEL; + interpolator = ACCELERATE_DECELERATE; } else if (endTarget == RECENTS) { interpolator = OVERSHOOT_1_2; } else { - interpolator = DEACCEL; + interpolator = DECELERATE; } if (endTarget.isLauncher) { @@ -1444,9 +1465,12 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, @Override public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { - if (task.taskId == mGestureState.getRunningTaskId() - && task.configuration.windowConfiguration.getActivityType() - != ACTIVITY_TYPE_HOME) { + boolean taskRunningAndNotHome = Arrays.stream(mGestureState + .getRunningTaskIds(true /*getMultipleTasks*/)) + .anyMatch(taskId -> task.taskId == taskId + && task.configuration.windowConfiguration.getActivityType() + != ACTIVITY_TYPE_HOME); + if (taskRunningAndNotHome) { // Since this is an edge case, just cancel and relaunch with default activity // options (since we don't know if there's an associated app icon to launch from) endRunningWindowAnim(true /* cancel */); @@ -1488,8 +1512,12 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, if (mGestureState.getEndTarget() == HOME) { getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs); + // Take first task ID, if there are multiple we don't have any special home + // animation so doesn't matter for splitscreen.. though the "allowEnterPip" might change + // depending on which task it is.. final RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null - ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId()) + ? mRecentsAnimationTargets + .findTask(mGestureState.getTopRunningTaskId()) : null; final ArrayList<IBinder> cookies = runningTaskTarget != null ? runningTaskTarget.taskInfo.launchCookies @@ -1518,8 +1546,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, // grab a screenshot before the PipContentOverlay gets parented on top of the task UI_HELPER_EXECUTOR.execute(() -> { - mTaskSnapshot = mRecentsAnimationController.screenshotTask( - mGestureState.getRunningTaskId()); + // Directly use top task, split to pip handled on shell side + final int taskId = mGestureState.getTopRunningTaskId(); + mTaskSnapshotCache.put(taskId, + mRecentsAnimationController.screenshotTask(taskId)); }); // let SystemUi reparent the overlay leash as soon as possible @@ -1725,11 +1755,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, private Rect getKeepClearAreaForHotseat() { Rect keepClearArea; - if (!ENABLE_PIP_KEEP_CLEAR_ALGORITHM) { - // make the height equal to hotseatBarSizePx only - keepClearArea = new Rect(0, 0, 0, mDp.hotseatBarSizePx); - return keepClearArea; - } // the keep clear area in global screen coordinates, in pixels if (mDp.isPhone) { if (mDp.isSeascape()) { @@ -1908,7 +1933,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, mActivityInitListener.unregister(); TaskStackChangeListeners.getInstance().unregisterTaskStackListener( mActivityRestartListener); - mTaskSnapshot = null; + mTaskSnapshotCache.clear(); } private void invalidateHandler() { @@ -1926,7 +1951,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, mActivityInitListener.unregister(); TaskStackChangeListeners.getInstance().unregisterTaskStackListener( mActivityRestartListener); - mTaskSnapshot = null; + mTaskSnapshotCache.clear(); } private void invalidateHandlerWithLauncher() { @@ -1981,37 +2006,39 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, // If there are no targets, then we don't need to capture anything mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); } else { - final int runningTaskId = mGestureState.getRunningTaskId(); boolean finishTransitionPosted = false; + // If we already have cached screenshot(s) from running tasks, skip update + boolean shouldUpdate = false; + int[] runningTaskIds = mGestureState.getRunningTaskIds(mIsSwipeForSplit); + for (int id : runningTaskIds) { + if (!mTaskSnapshotCache.containsKey(id)) { + shouldUpdate = true; + break; + } + } + if (mRecentsAnimationController != null) { // Update the screenshot of the task - if (mTaskSnapshot == null) { + if (shouldUpdate) { UI_HELPER_EXECUTOR.execute(() -> { if (mRecentsAnimationController == null) return; - final ThumbnailData taskSnapshot = - mRecentsAnimationController.screenshotTask(runningTaskId); - // If split case, we should update all split tasks snapshot - if (mIsSwipeForSplit) { - int[] splitTaskIds = TopTaskTracker.INSTANCE.get( - mContext).getRunningSplitTaskIds(); - for (int i = 0; i < splitTaskIds.length; i++) { - // Skip running one because done above. - if (splitTaskIds[i] == runningTaskId) continue; - - mRecentsAnimationController.screenshotTask(splitTaskIds[i]); - } + for (int id : runningTaskIds) { + mTaskSnapshotCache.put( + id, mRecentsAnimationController.screenshotTask(id)); } + MAIN_EXECUTOR.execute(() -> { - mTaskSnapshot = taskSnapshot; - if (!updateThumbnail(runningTaskId, false /* refreshView */)) { + if (!updateThumbnail(false /* refreshView */)) { setScreenshotCapturedState(); } }); }); return; } - finishTransitionPosted = updateThumbnail(runningTaskId, false /* refreshView */); + + finishTransitionPosted = updateThumbnail(false /* refreshView */); } + if (!finishTransitionPosted) { setScreenshotCapturedState(); } @@ -2019,35 +2046,34 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } // Returns whether finish transition was posted. - private boolean updateThumbnail(int runningTaskId, boolean refreshView) { - boolean finishTransitionPosted = false; - final TaskView taskView; + private boolean updateThumbnail(boolean refreshView) { if (mGestureState.getEndTarget() == HOME || mGestureState.getEndTarget() == NEW_TASK || mGestureState.getEndTarget() == ALL_APPS || mRecentsView == null) { // Capture the screenshot before finishing the transition to home or quickswitching to // ensure it's taken in the correct orientation, but no need to update the thumbnail. - taskView = null; - } else { - taskView = mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot, refreshView); + return false; } - if (taskView != null && refreshView && !mCanceled) { + + boolean finishTransitionPosted = false; + TaskView updatedTaskView = mRecentsView.updateThumbnail(mTaskSnapshotCache, refreshView); + if (updatedTaskView != null && refreshView && !mCanceled) { // Defer finishing the animation until the next launcher frame with the // new thumbnail - finishTransitionPosted = ViewUtils.postFrameDrawn(taskView, + finishTransitionPosted = ViewUtils.postFrameDrawn(updatedTaskView, () -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED), this::isCanceled); } + return finishTransitionPosted; } private void setScreenshotCapturedState() { // If we haven't posted a draw callback, set the state immediately. - Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT, - TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS); + TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT); mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); - TraceHelper.INSTANCE.endSection(traceToken); + TraceHelper.INSTANCE.endSection(); } private void finishCurrentTransitionToRecents() { @@ -2188,16 +2214,27 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, if (!mCanceled) { TaskView nextTask = mRecentsView == null ? null : mRecentsView.getNextPageTaskView(); if (nextTask != null) { - Task.TaskKey nextTaskKey = nextTask.getTask().key; - int taskId = nextTaskKey.id; - mGestureState.updateLastStartedTaskId(taskId); - boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds() - .contains(taskId); + int[] taskIds = nextTask.getTaskIds(); + StringBuilder nextTaskLog = new StringBuilder(); + for (TaskIdAttributeContainer c : nextTask.getTaskIdAttributeContainers()) { + if (c == null) { + continue; + } + nextTaskLog + .append("[id: ") + .append(c.getTask().key.id) + .append(", pkg: ") + .append(c.getTask().key.getPackageName()) + .append("] | "); + } + mGestureState.updateLastStartedTaskIds(taskIds); + boolean hasTaskPreviouslyAppeared = Arrays.stream(taskIds).anyMatch( + taskId -> mGestureState.getPreviouslyAppearedTaskIds() + .contains(taskId)); if (!hasTaskPreviouslyAppeared) { ActiveGestureLog.INSTANCE.trackEvent(EXPECTING_TASK_APPEARED); } - ActiveGestureLog.INSTANCE.addLog("Launching task: id=" + taskId - + " pkg=" + nextTaskKey.getPackageName()); + ActiveGestureLog.INSTANCE.addLog("Launching task: " + nextTaskLog); nextTask.launchTask(success -> { resultCallback.accept(success); if (success) { @@ -2267,7 +2304,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets) { if (mRecentsAnimationController != null) { boolean hasStartedTaskBefore = Arrays.stream(appearedTaskTargets).anyMatch( - targetCompat -> targetCompat.taskId == mGestureState.getLastStartedTaskId()); + mGestureState.mLastStartedTaskIdPredicate); if (!mStateCallback.hasStates(STATE_GESTURE_COMPLETED) && !hasStartedTaskBefore) { // This is a special case, if a task is started mid-gesture that wasn't a part of a // previous quickswitch task launch, then cancel the animation back to the app @@ -2280,8 +2317,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } else if (handleTaskAppeared(appearedTaskTargets)) { Optional<RemoteAnimationTarget> taskTargetOptional = Arrays.stream(appearedTaskTargets) - .filter(targetCompat -> - targetCompat.taskId == mGestureState.getLastStartedTaskId()) + .filter(mGestureState.mLastStartedTaskIdPredicate) .findFirst(); if (!taskTargetOptional.isPresent()) { ActiveGestureLog.INSTANCE.addLog("No appeared task matching started task id"); @@ -2350,11 +2386,16 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, * resume if we finish the controller. */ protected int getLastAppearedTaskIndex() { - return mRecentsView == null - ? -1 - : mGestureState.getLastAppearedTaskId() != -1 - ? mRecentsView.getTaskIndexForId(mGestureState.getLastAppearedTaskId()) - : mRecentsView.getRunningTaskIndex(); + if (mRecentsView == null) { + return -1; + } + + OptionalInt firstValidTaskId = Arrays.stream(mGestureState.getLastAppearedTaskIds()) + .filter(i -> i != -1) + .findFirst(); + return firstValidTaskId.isPresent() + ? mRecentsView.getTaskIndexForId(firstValidTaskId.getAsInt()) + : mRecentsView.getRunningTaskIndex(); } /** @@ -2362,7 +2403,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, * but before that task appeared. */ protected boolean hasStartedNewTask() { - return mGestureState.getLastStartedTaskId() != -1; + return mGestureState.getLastStartedTaskIds()[0] != -1; } /** @@ -2406,7 +2447,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, taskViewSimulator.apply(remoteHandle.getTransformParams()); } } - ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate(); } // Scaling of RecentsView during quick switch based on amount of recents scroll @@ -2428,11 +2468,11 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, if (scrollOffset < mQuickSwitchScaleScrollThreshold) { scaleProgress = Utilities.mapToRange(scrollOffset, 0, mQuickSwitchScaleScrollThreshold, - 0, maxScaleProgress, ACCEL_DEACCEL); + 0, maxScaleProgress, ACCELERATE_DECELERATE); } else if (scrollOffset > (maxScrollOffset - mQuickSwitchScaleScrollThreshold)) { scaleProgress = Utilities.mapToRange(scrollOffset, (maxScrollOffset - mQuickSwitchScaleScrollThreshold), maxScrollOffset, - maxScaleProgress, 0, ACCEL_DEACCEL); + maxScaleProgress, 0, ACCELERATE_DECELERATE); } return scaleProgress; @@ -2461,7 +2501,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, // "Catch up" with the displacement at mTaskbarCatchUpThreshold. if (displacement < mTaskbarCatchUpThreshold) { return Utilities.mapToRange(displacement, mTaskbarAppWindowThreshold, - mTaskbarCatchUpThreshold, 0, mTaskbarCatchUpThreshold, ACCEL_DEACCEL); + mTaskbarCatchUpThreshold, 0, mTaskbarCatchUpThreshold, ACCELERATE_DECELERATE); } return displacement; @@ -2476,26 +2516,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, mRecentsAnimationTargets.nonApps, shown, null /* animatorHandler */); } - /** - * Used for winscope tracing, see launcher_trace.proto - * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto - * @param inputConsumerProto The parent of this proto message. - */ - public void writeToProto(InputConsumerProto.Builder inputConsumerProto) { - SwipeHandlerProto.Builder swipeHandlerProto = SwipeHandlerProto.newBuilder(); - - mGestureState.writeToProto(swipeHandlerProto); - - swipeHandlerProto.setIsRecentsAttachedToAppWindow( - mAnimationFactory.isRecentsAttachedToAppWindow()); - swipeHandlerProto.setScrollOffset(mRecentsView == null - ? 0 - : mRecentsView.getScrollOffset()); - swipeHandlerProto.setAppToOverviewProgress(mCurrentShift.value); - - inputConsumerProto.setSwipeHandler(swipeHandlerProto); - } - public interface Factory { AbsSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs); } diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index 60083c67e7..5a9d80d630 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -15,11 +15,11 @@ */ package com.android.quickstep; +import static com.android.app.animation.Interpolators.ACCELERATE_2; +import static com.android.app.animation.Interpolators.INSTANT; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR; import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe; -import static com.android.launcher3.anim.Interpolators.ACCEL_2; -import static com.android.launcher3.anim.Interpolators.INSTANT; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.AbsSwipeUpHandler.RECENTS_ATTACH_DURATION; import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK; import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS; @@ -553,7 +553,7 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T long animationDuration = animate ? RECENTS_ATTACH_DURATION : 0; Animator fadeAnim = mActivity.getStateManager() .createStateElementAnimation(INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0); - fadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2); + fadeAnim.setInterpolator(attached ? INSTANT : ACCELERATE_2); fadeAnim.setDuration(animationDuration); animatorSet.play(fadeAnim); diff --git a/quickstep/src/com/android/quickstep/BinderTracker.java b/quickstep/src/com/android/quickstep/BinderTracker.java new file mode 100644 index 0000000000..a876cd868b --- /dev/null +++ b/quickstep/src/com/android/quickstep/BinderTracker.java @@ -0,0 +1,187 @@ +/** + * 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.quickstep; + +import static android.os.IBinder.FLAG_ONEWAY; + +import android.os.Binder; +import android.os.Binder.ProxyTransactListener; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.os.Trace; +import android.util.Log; + +import androidx.annotation.Nullable; + +import com.android.launcher3.util.SafeCloseable; +import com.android.launcher3.util.TraceHelper; + +import java.util.LinkedList; +import java.util.Set; +import java.util.function.Consumer; + +import kotlin.random.Random; + +/** + * A binder proxy transaction listener for tracking binder calls on main thread. + */ +public class BinderTracker { + + private static final String TAG = "BinderTracker"; + + // Common IPCs that are ok to block the main thread. + private static final Set<String> sAllowedFrameworkClasses = Set.of( + "android.view.IWindowSession", + "android.os.IPowerManager", + "android.os.IServiceManager"); + + /** + * Starts tracking binder class and returns a {@link SafeCloseable} to end tracking + */ + public static SafeCloseable startTracking(Consumer<BinderCallSite> callback) { + TraceHelper current = TraceHelper.INSTANCE; + + TraceHelperExtension helper = new TraceHelperExtension(callback); + TraceHelper.INSTANCE = helper; + Binder.setProxyTransactListener(helper); + + return () -> { + Binder.setProxyTransactListener(null); + TraceHelper.INSTANCE = current; + }; + } + + private static final LinkedList<String> mMainThreadTraceStack = new LinkedList<>(); + private static final LinkedList<String> mMainThreadIgnoreIpcStack = new LinkedList<>(); + + private static class TraceHelperExtension extends TraceHelper implements ProxyTransactListener { + + private final Consumer<BinderCallSite> mUnexpectedTransactionCallback; + + TraceHelperExtension(Consumer<BinderCallSite> unexpectedTransactionCallback) { + mUnexpectedTransactionCallback = unexpectedTransactionCallback; + } + + @Override + public void beginSection(String sectionName) { + if (isMainThread()) { + mMainThreadTraceStack.add(sectionName); + } + super.beginSection(sectionName); + } + + @Override + public SafeCloseable beginAsyncSection(String sectionName) { + if (!isMainThread()) { + return super.beginAsyncSection(sectionName); + } + + mMainThreadTraceStack.add(sectionName); + int cookie = Random.Default.nextInt(); + Trace.beginAsyncSection(sectionName, cookie); + return () -> { + Trace.endAsyncSection(sectionName, cookie); + mMainThreadTraceStack.remove(sectionName); + }; + } + + @Override + public void endSection() { + super.endSection(); + if (isMainThread()) { + mMainThreadTraceStack.pollLast(); + } + } + + @Override + public SafeCloseable allowIpcs(String rpcName) { + if (!isMainThread()) { + return super.allowIpcs(rpcName); + } + + mMainThreadTraceStack.add(rpcName); + mMainThreadIgnoreIpcStack.add(rpcName); + int cookie = Random.Default.nextInt(); + Trace.beginAsyncSection(rpcName, cookie); + return () -> { + Trace.endAsyncSection(rpcName, cookie); + mMainThreadTraceStack.remove(rpcName); + mMainThreadIgnoreIpcStack.remove(rpcName); + }; + } + + @Override + public Object onTransactStarted(IBinder binder, int transactionCode, int flags) { + if (!isMainThread() || (flags & FLAG_ONEWAY) == FLAG_ONEWAY) { + return null; + } + + String ipcBypass = mMainThreadIgnoreIpcStack.peekLast(); + String descriptor; + try { + descriptor = binder.getInterfaceDescriptor(); + if (sAllowedFrameworkClasses.contains(descriptor)) { + return null; + } + } catch (RemoteException e) { + Log.e(TAG, "Error getting IPC descriptor", e); + descriptor = binder.getClass().getSimpleName(); + } + + if (ipcBypass == null) { + mUnexpectedTransactionCallback.accept(new BinderCallSite( + mMainThreadTraceStack.peekLast(), descriptor, transactionCode)); + } else { + Log.d(TAG, "MainThread-IPC " + descriptor + " ignored due to " + ipcBypass); + } + return null; + } + + @Override + public Object onTransactStarted(IBinder binder, int transactionCode) { + // Do nothing + return null; + } + + @Override + public void onTransactEnded(Object session) { + // Do nothing + } + } + + private static boolean isMainThread() { + return Thread.currentThread() == Looper.getMainLooper().getThread(); + } + + /** + * Information about a binder call + */ + public static class BinderCallSite { + + @Nullable + public final String activeTrace; + public final String descriptor; + public final int transactionCode; + + BinderCallSite(String activeTrace, String descriptor, int transactionCode) { + this.activeTrace = activeTrace; + this.descriptor = descriptor; + this.transactionCode = transactionCode; + } + } +} diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java index eff53f3c85..c5a88bc073 100644 --- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java @@ -19,13 +19,13 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.content.Intent.EXTRA_COMPONENT_NAME; import static android.content.Intent.EXTRA_USER; +import static com.android.app.animation.Interpolators.ACCELERATE; import static com.android.launcher3.GestureNavContract.EXTRA_GESTURE_CONTRACT; import static com.android.launcher3.GestureNavContract.EXTRA_ICON_POSITION; import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE; import static com.android.launcher3.GestureNavContract.EXTRA_ON_FINISH_CALLBACK; import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK; import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; -import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely; import android.animation.ObjectAnimator; @@ -296,7 +296,7 @@ public class FallbackSwipeHandler extends @Override public AnimatorPlaybackController createActivityAnimationToHome() { PendingAnimation pa = new PendingAnimation(mDuration); - pa.setFloat(mRecentsAlpha, AnimatedFloat.VALUE, 0, ACCEL); + pa.setFloat(mRecentsAlpha, AnimatedFloat.VALUE, 0, ACCELERATE); return pa.createPlaybackController(); } @@ -324,7 +324,7 @@ public class FallbackSwipeHandler extends @Override public void playAtomicAnimation(float velocity) { ObjectAnimator alphaAnim = mHomeAlpha.animateToValue(mHomeAlpha.value, 1); - alphaAnim.setDuration(mDuration).setInterpolator(ACCEL); + alphaAnim.setDuration(mDuration).setInterpolator(ACCELERATE); alphaAnim.start(); if (mRunningOverHome) { diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java index 9d7ccb42e4..c2d8c6214e 100644 --- a/quickstep/src/com/android/quickstep/GestureState.java +++ b/quickstep/src/com/android/quickstep/GestureState.java @@ -15,8 +15,9 @@ */ package com.android.quickstep; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; + import static com.android.launcher3.MotionEventsUtils.isTrackpadFourFingerSwipe; -import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe; import static com.android.launcher3.MotionEventsUtils.isTrackpadThreeFingerSwipe; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; @@ -37,8 +38,6 @@ import android.view.RemoteAnimationTarget; import com.android.launcher3.statemanager.BaseState; import com.android.launcher3.statemanager.StatefulActivity; -import com.android.launcher3.tracing.GestureStateProto; -import com.android.launcher3.tracing.SwipeHandlerProto; import com.android.quickstep.TopTaskTracker.CachedTaskInfo; import com.android.quickstep.util.ActiveGestureErrorDetector; import com.android.quickstep.util.ActiveGestureLog; @@ -46,10 +45,12 @@ import com.android.systemui.shared.recents.model.ThumbnailData; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.Predicate; /** * Manages the state for an active system gesture, listens for events from the system and Launcher, @@ -58,28 +59,37 @@ import java.util.Set; @TargetApi(Build.VERSION_CODES.R) public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationListener { + final Predicate<RemoteAnimationTarget> mLastStartedTaskIdPredicate = new Predicate<>() { + @Override + public boolean test(RemoteAnimationTarget targetCompat) { + for (int taskId : mLastStartedTaskId) { + if (targetCompat.taskId == taskId) { + return true; + } + } + return false; + } + }; + /** * Defines the end targets of a gesture and the associated state. */ public enum GestureEndTarget { - HOME(true, LAUNCHER_STATE_HOME, false, GestureStateProto.GestureEndTarget.HOME), + HOME(true, LAUNCHER_STATE_HOME, false), - RECENTS(true, LAUNCHER_STATE_OVERVIEW, true, GestureStateProto.GestureEndTarget.RECENTS), + RECENTS(true, LAUNCHER_STATE_OVERVIEW, true), - NEW_TASK(false, LAUNCHER_STATE_BACKGROUND, true, - GestureStateProto.GestureEndTarget.NEW_TASK), + NEW_TASK(false, LAUNCHER_STATE_BACKGROUND, true), - LAST_TASK(false, LAUNCHER_STATE_BACKGROUND, true, - GestureStateProto.GestureEndTarget.LAST_TASK), + LAST_TASK(false, LAUNCHER_STATE_BACKGROUND, true), - ALL_APPS(true, LAUNCHER_STATE_ALLAPPS, false, GestureStateProto.GestureEndTarget.ALL_APPS); + ALL_APPS(true, LAUNCHER_STATE_ALLAPPS, false); - GestureEndTarget(boolean isLauncher, int containerType, boolean recentsAttachedToAppWindow, - GestureStateProto.GestureEndTarget protoEndTarget) { + GestureEndTarget(boolean isLauncher, int containerType, + boolean recentsAttachedToAppWindow) { this.isLauncher = isLauncher; this.containerType = containerType; this.recentsAttachedToAppWindow = recentsAttachedToAppWindow; - this.protoEndTarget = protoEndTarget; } /** Whether the target is in the launcher activity. Implicitly, if the end target is going @@ -89,8 +99,6 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL public final int containerType; /** Whether RecentsView should be attached to the window as we animate to this target */ public final boolean recentsAttachedToAppWindow; - /** The GestureStateProto enum value, used for winscope tracing. See launcher_trace.proto */ - public final GestureStateProto.GestureEndTarget protoEndTarget; } private static final String TAG = "GestureState"; @@ -150,15 +158,10 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL public enum TrackpadGestureType { NONE, - // Assigned before we know whether it's a 3-finger or 4-finger gesture. - MULTI_FINGER, THREE_FINGER, FOUR_FINGER; public static TrackpadGestureType getTrackpadGestureType(MotionEvent event) { - if (!isTrackpadMultiFingerSwipe(event)) { - return TrackpadGestureType.NONE; - } if (isTrackpadThreeFingerSwipe(event)) { return TrackpadGestureType.THREE_FINGER; } @@ -166,16 +169,16 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL return TrackpadGestureType.FOUR_FINGER; } - return TrackpadGestureType.MULTI_FINGER; + return TrackpadGestureType.NONE; } } private TrackpadGestureType mTrackpadGestureType = TrackpadGestureType.NONE; private CachedTaskInfo mRunningTask; private GestureEndTarget mEndTarget; - private RemoteAnimationTarget mLastAppearedTaskTarget; + private RemoteAnimationTarget[] mLastAppearedTaskTargets; private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>(); - private int mLastStartedTaskId = -1; + private int[] mLastStartedTaskId = new int[]{INVALID_TASK_ID, INVALID_TASK_ID}; private RecentsAnimationController mRecentsAnimationController; private HashMap<Integer, ThumbnailData> mRecentsAnimationCanceledSnapshots; @@ -201,7 +204,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL mGestureId = other.mGestureId; mRunningTask = other.mRunningTask; mEndTarget = other.mEndTarget; - mLastAppearedTaskTarget = other.mLastAppearedTaskTarget; + mLastAppearedTaskTargets = other.mLastAppearedTaskTargets; mPreviouslyAppearedTaskIds = other.mPreviouslyAppearedTaskIds; mLastStartedTaskId = other.mLastStartedTaskId; } @@ -305,10 +308,29 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL } /** - * @return the running task id for this gesture. + * @param getMultipleTasks Whether multiple tasks or not are to be returned (for split) + * @return the running task ids for this gesture. */ - public int getRunningTaskId() { - return mRunningTask != null ? mRunningTask.getTaskId() : -1; + public int[] getRunningTaskIds(boolean getMultipleTasks) { + if (mRunningTask == null) { + return new int[]{INVALID_TASK_ID, INVALID_TASK_ID}; + } else { + int cachedTasksSize = mRunningTask.mAllCachedTasks.size(); + int count = Math.min(cachedTasksSize, getMultipleTasks ? 2 : 1); + int[] runningTaskIds = new int[count]; + for (int i = 0; i < count; i++) { + runningTaskIds[i] = mRunningTask.mAllCachedTasks.get(i).taskId; + } + return runningTaskIds; + } + } + + /** + * @see #getRunningTaskIds(boolean) + * @return the single top-most running taskId for this gesture + */ + public int getTopRunningTaskId() { + return getRunningTaskIds(false /*getMultipleTasks*/)[0]; } /** @@ -321,18 +343,26 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL /** * Updates the last task that appeared during this gesture. */ - public void updateLastAppearedTaskTarget(RemoteAnimationTarget lastAppearedTaskTarget) { - mLastAppearedTaskTarget = lastAppearedTaskTarget; - if (lastAppearedTaskTarget != null) { - mPreviouslyAppearedTaskIds.add(lastAppearedTaskTarget.taskId); + public void updateLastAppearedTaskTargets(RemoteAnimationTarget[] lastAppearedTaskTargets) { + mLastAppearedTaskTargets = lastAppearedTaskTargets; + for (RemoteAnimationTarget target : lastAppearedTaskTargets) { + if (target == null) { + continue; + } + mPreviouslyAppearedTaskIds.add(target.taskId); } } /** * @return The id of the task that appeared during this gesture. */ - public int getLastAppearedTaskId() { - return mLastAppearedTaskTarget != null ? mLastAppearedTaskTarget.taskId : -1; + public int[] getLastAppearedTaskIds() { + if (mLastAppearedTaskTargets == null) { + return new int[]{INVALID_TASK_ID, INVALID_TASK_ID}; + } else { + return Arrays.stream(mLastAppearedTaskTargets) + .mapToInt(target -> target != null ? target.taskId : INVALID_TASK_ID).toArray(); + } } public void updatePreviouslyAppearedTaskIds(Set<Integer> previouslyAppearedTaskIds) { @@ -346,7 +376,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL /** * Updates the last task that we started via startActivityFromRecents() during this gesture. */ - public void updateLastStartedTaskId(int lastStartedTaskId) { + public void updateLastStartedTaskIds(int[] lastStartedTaskId) { mLastStartedTaskId = lastStartedTaskId; } @@ -354,7 +384,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL * @return The id of the task that was most recently started during this gesture, or -1 if * no task has been started yet (i.e. we haven't settled on a new task). */ - public int getLastStartedTaskId() { + public int[] getLastStartedTaskIds() { return mLastStartedTaskId; } @@ -490,21 +520,8 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL pw.println(" gestureID=" + mGestureId); pw.println(" runningTask=" + mRunningTask); pw.println(" endTarget=" + mEndTarget); - pw.println(" lastAppearedTaskTargetId=" + getLastAppearedTaskId()); - pw.println(" lastStartedTaskId=" + mLastStartedTaskId); + pw.println(" lastAppearedTaskTargetId=" + Arrays.toString(mLastAppearedTaskTargets)); + pw.println(" lastStartedTaskId=" + Arrays.toString(mLastStartedTaskId)); pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning()); } - - /** - * Used for winscope tracing, see launcher_trace.proto - * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto - * @param swipeHandlerProto The parent of this proto message. - */ - public void writeToProto(SwipeHandlerProto.Builder swipeHandlerProto) { - GestureStateProto.Builder gestureStateProto = GestureStateProto.newBuilder(); - gestureStateProto.setEndTarget(mEndTarget == null - ? GestureStateProto.GestureEndTarget.UNSET - : mEndTarget.protoEndTarget); - swipeHandlerProto.setGestureState(gestureStateProto); - } } diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java index 6b189cf4d6..23def14515 100644 --- a/quickstep/src/com/android/quickstep/InputConsumer.java +++ b/quickstep/src/com/android/quickstep/InputConsumer.java @@ -21,9 +21,6 @@ import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; -import com.android.launcher3.tracing.InputConsumerProto; -import com.android.launcher3.tracing.TouchInteractionServiceProto; - @TargetApi(Build.VERSION_CODES.O) public interface InputConsumer { @@ -42,6 +39,7 @@ public interface InputConsumer { int TYPE_TASKBAR_STASH = 1 << 12; int TYPE_STATUS_BAR = 1 << 13; int TYPE_CURSOR_HOVER = 1 << 14; + int TYPE_NAV_HANDLE_LONG_PRESS = 1 << 15; String[] NAMES = new String[] { "TYPE_NO_OP", // 0 @@ -59,6 +57,7 @@ public interface InputConsumer { "TYPE_TASKBAR_STASH", // 12 "TYPE_STATUS_BAR", // 13 "TYPE_CURSOR_HOVER", // 14 + "TYPE_NAV_HANDLE_LONG_PRESS", // 15 }; InputConsumer NO_OP = () -> TYPE_NO_OP; @@ -127,21 +126,4 @@ public interface InputConsumer { } return name.toString(); } - - /** - * Used for winscope tracing, see launcher_trace.proto - * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto - * @param serviceProto The parent of this proto message. - */ - default void writeToProto(TouchInteractionServiceProto.Builder serviceProto) { - InputConsumerProto.Builder inputConsumerProto = InputConsumerProto.newBuilder(); - inputConsumerProto.setName(getName()); - writeToProtoInternal(inputConsumerProto); - serviceProto.setInputConsumer(inputConsumerProto); - } - - /** - * @see #writeToProto - allows subclasses to write additional info to the proto. - */ - default void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {} } diff --git a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java index 7638541023..33a2366157 100644 --- a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java +++ b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java @@ -20,6 +20,9 @@ import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.os.Process; +import android.os.UserHandle; +import android.util.Log; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.util.InstantAppResolver; @@ -49,4 +52,18 @@ public class InstantAppResolverImpl extends InstantAppResolver { ComponentName cn = info.getTargetComponent(); return cn != null && cn.getClassName().equals(COMPONENT_CLASS_MARKER); } + + @Override + public boolean isInstantApp(String packageName, int userId) { + if (!Process.myUserHandle().equals(UserHandle.of(userId))) { + // Instant app can only exist on current user + return false; + } + try { + return mPM.isInstantApp(packageName); + } catch (Exception e) { + Log.e(TAG, "Failed to determine whether package is instant app " + packageName, e); + return false; + } + } } diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java index 0e0b02262b..5784c379cd 100644 --- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java +++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java @@ -15,12 +15,13 @@ */ package com.android.quickstep; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.BACKGROUND_APP; +import static com.android.launcher3.LauncherState.FLOATING_SEARCH_BAR; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE; @@ -267,7 +268,9 @@ public final class LauncherActivityInterface extends @Override public boolean allowAllAppsFromOverview() { - return FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get(); + return FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get() + // If floating search bar would not show in overview, don't allow all apps gesture. + && OVERVIEW.areElementsVisible(getCreatedActivity(), FLOATING_SEARCH_BAR); } @Override diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java index c18ad5a626..f1660ee858 100644 --- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java +++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java @@ -129,7 +129,7 @@ public class LauncherBackAnimationController { mWindowMaxDeltaY = mLauncher.getResources().getDimensionPixelSize( R.dimen.swipe_back_window_max_delta_y); mCancelInterpolator = - AnimationUtils.loadInterpolator(mLauncher, R.interpolator.back_cancel); + AnimationUtils.loadInterpolator(mLauncher, R.interpolator.standard_interpolator); } /** diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java index fd5c1a7a78..a9d8afcecf 100644 --- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java +++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java @@ -15,10 +15,10 @@ */ package com.android.quickstep; +import static com.android.app.animation.Interpolators.EXAGGERATED_EASE; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.Utilities.mapBoundToRange; -import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; import static com.android.launcher3.views.FloatingIconView.getFloatingIconView; diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java index 07db194ff4..42bf1ac2ed 100644 --- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java +++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java @@ -37,7 +37,6 @@ import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.taskbar.TaskbarUIController; import com.android.launcher3.util.RunnableList; import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener; -import com.android.quickstep.views.DesktopTaskView; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -141,6 +140,11 @@ public class OverviewCommandHelper { mPendingCommands.clear(); } + @UiThread + public boolean canStartHomeSafely() { + return mPendingCommands.isEmpty() || mPendingCommands.get(0).type == TYPE_HOME; + } + @Nullable private TaskView getNextTask(RecentsView view) { final TaskView runningTaskView = view.getRunningTaskView(); @@ -186,11 +190,6 @@ public class OverviewCommandHelper { && dp != null && (dp.isTablet || dp.isTwoPanels); - if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) { - // TODO(b/268075592): add support for quickswitch to/from desktop - allowQuickSwitch = false; - } - if (cmd.type == TYPE_HIDE) { if (!allowQuickSwitch) { return true; diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java index a8f3c3af46..60713cf28f 100644 --- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java +++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java @@ -38,8 +38,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.R; -import com.android.launcher3.tracing.OverviewComponentObserverProto; -import com.android.launcher3.tracing.TouchInteractionServiceProto; import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.systemui.shared.system.PackageManagerWrapper; @@ -276,19 +274,6 @@ public final class OverviewComponentObserver { } /** - * Used for winscope tracing, see launcher_trace.proto - * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto - * @param serviceProto The parent of this proto message. - */ - public void writeToProto(TouchInteractionServiceProto.Builder serviceProto) { - OverviewComponentObserverProto.Builder overviewComponentObserver = - OverviewComponentObserverProto.newBuilder(); - overviewComponentObserver.setOverviewActivityStarted(mActivityInterface.isStarted()); - overviewComponentObserver.setOverviewActivityResumed(mActivityInterface.isResumed()); - serviceProto.setOverviewComponentObvserver(overviewComponentObserver); - } - - /** * Starts the intent for the current home activity. */ public static void startHomeIntentSafely(@NonNull Context context, @Nullable Bundle options) { diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java index 5f589bfa50..d1939efa78 100644 --- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java +++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java @@ -19,6 +19,8 @@ import android.annotation.TargetApi; import android.content.Context; import android.content.pm.PackageManager; import android.os.Build; +import android.os.Looper; +import android.os.Trace; import android.os.UserManager; import android.util.Log; import android.view.ThreadedRenderer; @@ -60,5 +62,14 @@ public class QuickstepProcessInitializer extends MainProcessInitializer { // Elevate GPU priority for Quickstep and Remote animations. ThreadedRenderer.setContextPriority( ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG); + + // Enable Looper trace points. + // This allows us to see Handler callbacks on traces. + Looper.getMainLooper().setTraceTag(Trace.TRACE_TAG_APP); + + if (BuildConfig.IS_STUDIO_BUILD) { + BinderTracker.startTracking(call -> Log.e("BinderCall", + call.descriptor + " called on mainthread under " + call.activeTrace)); + } } } diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java index 4c9cf8b897..031d409ece 100644 --- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java +++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java @@ -80,8 +80,7 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { } case TestProtocol.REQUEST_HAS_TIS: { - response.putBoolean( - TestProtocol.REQUEST_HAS_TIS, true); + response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, true); return response; } diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index 38ac5bb9e2..0a6fefaa34 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -16,6 +16,8 @@ package com.android.quickstep; +import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; + import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.views.DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED; import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM; @@ -269,6 +271,7 @@ public class RecentTasksList { TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size()); + int numVisibleTasks = 0; for (GroupedRecentTaskInfo rawTask : rawTasks) { if (DESKTOP_IS_PROTO2_ENABLED && rawTask.getType() == TYPE_FREEFORM) { GroupTask desktopTask = createDesktopTask(rawTask); @@ -285,12 +288,27 @@ public class RecentTasksList { task1.setLastSnapshotData(taskInfo1); Task task2 = null; if (taskInfo2 != null) { + // Is split task Task.TaskKey task2Key = new Task.TaskKey(taskInfo2); task2 = loadKeysOnly ? new Task(task2Key) : Task.from(task2Key, taskInfo2, tmpLockedUsers.get(task2Key.userId) /* isLocked */); task2.setLastSnapshotData(taskInfo2); + } else { + // Is fullscreen task + if (numVisibleTasks > 0) { + boolean isExcluded = (taskInfo1.baseIntent.getFlags() + & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0; + if (taskInfo1.isTopActivityTransparent && isExcluded) { + // If there are already visible tasks, then ignore the excluded tasks and + // don't add them to the returned list + continue; + } + } + } + if (taskInfo1.isVisible) { + numVisibleTasks++; } final SplitConfigurationOptions.SplitBounds launcherSplitBounds = convertSplitBounds(rawTask.getSplitBounds()); @@ -308,7 +326,6 @@ public class RecentTasksList { task.setLastSnapshotData(taskInfo); task.positionInParent = taskInfo.positionInParent; task.appBounds = taskInfo.configuration.windowConfiguration.getAppBounds(); - // TODO(b/244348395): tasks should be sorted from oldest to most recently used tasks.add(task); } return new DesktopTask(tasks); diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index 39af7fd6dd..72439dec26 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -22,7 +22,6 @@ import static android.view.RemoteAnimationTarget.MODE_OPENING; import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION; import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION; import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY; -import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS; import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL; import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely; import static com.android.quickstep.TaskUtils.taskIsATargetWithMode; @@ -38,6 +37,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Trace; +import android.util.Log; import android.view.Display; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; @@ -46,8 +46,10 @@ import android.view.View; import android.window.RemoteTransition; import android.window.SplashScreen; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; @@ -56,7 +58,6 @@ import com.android.launcher3.LauncherAnimationRunner.AnimationResult; import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory; import com.android.launcher3.R; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.model.data.ItemInfo; @@ -66,6 +67,7 @@ import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.taskbar.FallbackTaskbarUIController; import com.android.launcher3.taskbar.TaskbarManager; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.ActivityOptionsWrapper; import com.android.launcher3.util.ActivityTracker; import com.android.launcher3.util.RunnableList; @@ -107,7 +109,6 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { private FallbackRecentsView mFallbackRecentsView; private OverviewActionsView mActionsView; private TISBindHelper mTISBindHelper; - private @Nullable TaskbarManager mTaskbarManager; private @Nullable FallbackTaskbarUIController mTaskbarUIController; private StateManager<RecentsState> mStateManager; @@ -119,6 +120,7 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { // animation callback private final Handler mHandler = new Handler(); private final Runnable mAnimationStartTimeoutRunnable = this::onAnimationStartTimeout; + private SplitSelectStateController mSplitSelectStateController; /** * Init drag layer and overview panel views. @@ -130,22 +132,21 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { mScrimView = findViewById(R.id.scrim_view); mFallbackRecentsView = findViewById(R.id.overview_panel); mActionsView = findViewById(R.id.overview_actions_view); - SYSUI_PROGRESS.set(getRootView().getSysUiScrim(), 0f); - - SplitSelectStateController controller = + getRootView().getSysUiScrim().getSysUIProgress().updateValue(0); + mSplitSelectStateController = new SplitSelectStateController(this, mHandler, getStateManager(), - null /* depthController */, getStatsLogManager(), + null /* depthController */, getStatsLogManager(), SystemUiProxy.INSTANCE.get(this), RecentsModel.INSTANCE.get(this)); mDragLayer.recreateControllers(); - mFallbackRecentsView.init(mActionsView, controller); + mFallbackRecentsView.init(mActionsView, mSplitSelectStateController); mTISBindHelper = new TISBindHelper(this, this::onTISConnected); } private void onTISConnected(TouchInteractionService.TISBinder binder) { - mTaskbarManager = binder.getTaskbarManager(); - if (mTaskbarManager != null) { - mTaskbarManager.setActivity(this); + TaskbarManager taskbarManager = binder.getTaskbarManager(); + if (taskbarManager != null) { + taskbarManager.setActivity(this); } } @@ -241,6 +242,11 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { } final TaskView taskView = (TaskView) v; + final RecentsView recentsView = taskView.getRecentsView(); + if (recentsView == null) { + return super.getActivityLaunchOptions(v, item); + } + RunnableList onEndCallback = new RunnableList(); mActivityLaunchAnimationRunner = new RemoteAnimationFactory() { @@ -249,7 +255,7 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, AnimationResult result) { mHandler.removeCallbacks(mAnimationStartTimeoutRunnable); - AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets, + AnimatorSet anim = composeRecentsLaunchAnimator(recentsView, taskView, appTargets, wallpaperTargets, nonAppTargets); anim.addListener(resetStateListener()); result.setAnimation(anim, RecentsActivity.this, onEndCallback::executeAllAndDestroy, @@ -277,6 +283,8 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { activityOptions.options.setLaunchDisplayId( (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId() : Display.DEFAULT_DISPLAY); + activityOptions.options.setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); mHandler.postDelayed(mAnimationStartTimeoutRunnable, RECENTS_ANIMATION_TIMEOUT); return activityOptions; } @@ -284,14 +292,16 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { /** * Composes the animations for a launch from the recents list if possible. */ - private AnimatorSet composeRecentsLaunchAnimator(TaskView taskView, + private AnimatorSet composeRecentsLaunchAnimator( + @NonNull RecentsView recentsView, + @NonNull TaskView taskView, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets) { AnimatorSet target = new AnimatorSet(); boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING); PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION); - createRecentsWindowAnimator(taskView, !activityClosing, appTargets, + createRecentsWindowAnimator(recentsView, taskView, !activityClosing, appTargets, wallpaperTargets, nonAppTargets, null /* depthController */, pa); target.play(pa.buildAnim()); @@ -299,7 +309,7 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { if (activityClosing) { Animator adjacentAnimation = mFallbackRecentsView .createAdjacentPageAnimForTaskLaunch(taskView); - adjacentAnimation.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR); + adjacentAnimation.setInterpolator(Interpolators.TOUCH_RESPONSE); adjacentAnimation.setDuration(RECENTS_LAUNCH_DURATION); adjacentAnimation.addListener(resetStateListener()); target.play(adjacentAnimation); @@ -384,11 +394,8 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { super.onDestroy(); ACTIVITY_TRACKER.onActivityDestroyed(this); mActivityLaunchAnimationRunner = null; - + mSplitSelectStateController.onDestroy(); mTISBindHelper.onDestroy(); - if (mTaskbarManager != null) { - mTaskbarManager.clearActivity(this); - } } @Override @@ -398,6 +405,7 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { } public void startHome() { + Log.d(TestProtocol.INCORRECT_HOME_STATE, "start home from recents activity"); RecentsView recentsView = getOverviewPanel(); recentsView.switchToScreenshot(() -> recentsView.finishRecentsAnimation(true, this::startHomeInternal)); @@ -471,4 +479,9 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { } }; } + + public boolean canStartHomeSafely() { + OverviewCommandHelper overviewCommandHelper = mTISBindHelper.getOverviewCommandHelper(); + return overviewCommandHelper == null || overviewCommandHelper.canStartHomeSafely(); + } } diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index f8e09e1e88..8972dc84c1 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -115,8 +115,8 @@ public class RecentsAnimationController { * {@link RecentsAnimationCallbacks#onTasksAppeared}}. */ @UiThread - public void removeTaskTarget(@NonNull RemoteAnimationTarget target) { - UI_HELPER_EXECUTOR.execute(() -> mController.removeTask(target.taskId)); + public void removeTaskTarget(int targetTaskId) { + UI_HELPER_EXECUTOR.execute(() -> mController.removeTask(targetTaskId)); } @UiThread @@ -167,7 +167,6 @@ public class RecentsAnimationController { /* event= */ "finishRecentsAnimation", /* extras= */ toRecents, /* gestureEvent= */ FINISH_RECENTS_ANIMATION); - // Finish not yet requested mFinishRequested = true; mFinishTargetIsLauncher = toRecents; diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index 64ec1d8a6d..1448a523b0 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -17,7 +17,6 @@ package com.android.quickstep; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.content.Intent.ACTION_USER_UNLOCKED; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.launcher3.util.DisplayController.CHANGE_ALL; @@ -52,10 +51,8 @@ import android.content.Context; import android.graphics.Region; import android.inputmethodservice.InputMethodService; import android.net.Uri; -import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; -import android.os.UserManager; import android.provider.Settings; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -68,7 +65,6 @@ import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener; import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.NavigationMode; import com.android.launcher3.util.SettingsCache; -import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.quickstep.TopTaskTracker.CachedTaskInfo; import com.android.quickstep.util.NavBarPosition; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -116,15 +112,6 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { private final boolean mIsOneHandedModeSupported; private boolean mPipIsActive; - private boolean mIsUserUnlocked; - private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>(); - private final SimpleBroadcastReceiver mUserUnlockedReceiver = new SimpleBroadcastReceiver(i -> { - if (ACTION_USER_UNLOCKED.equals(i.getAction())) { - mIsUserUnlocked = true; - notifyUserUnlocked(); - } - }); - private int mGestureBlockingTaskId = -1; private @NonNull Region mExclusionRegion = new Region(); private SystemGestureExclusionListenerCompat mExclusionListener; @@ -150,14 +137,6 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { runOnDestroy(mRotationTouchHelper::destroy); } - // Register for user unlocked if necessary - mIsUserUnlocked = context.getSystemService(UserManager.class) - .isUserUnlocked(Process.myUserHandle()); - if (!mIsUserUnlocked) { - mUserUnlockedReceiver.register(mContext, ACTION_USER_UNLOCKED); - } - runOnDestroy(() -> mUserUnlockedReceiver.unregisterReceiverSafely(mContext)); - // Register for exclusion updates mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) { @Override @@ -317,39 +296,12 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { } /** - * Adds a callback for when a user is unlocked. If the user is already unlocked, this listener - * will be called back immediately. - */ - public void runOnUserUnlocked(Runnable action) { - if (mIsUserUnlocked) { - action.run(); - } else { - mUserUnlockedActions.add(action); - } - } - - /** - * @return whether the user is unlocked. - */ - public boolean isUserUnlocked() { - return mIsUserUnlocked; - } - - /** * @return whether the user has completed setup wizard */ public boolean isUserSetupComplete() { return mIsUserSetupComplete; } - private void notifyUserUnlocked() { - for (Runnable action : mUserUnlockedActions) { - action.run(); - } - mUserUnlockedActions.clear(); - mUserUnlockedReceiver.unregisterReceiverSafely(mContext); - } - /** * Sets the task id where gestures should be blocked */ @@ -386,8 +338,16 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { boolean canStartWithNavHidden = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0 || (mSystemUiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0 || mRotationTouchHelper.isTaskListFrozen(); - return canStartWithNavHidden - && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0 + return canStartWithNavHidden && canStartTrackpadGesture(); + } + + /** + * @return whether SystemUI is in a state where we can start a system gesture from the trackpad. + * Trackpad gestures can start even when the nav bar / task bar is hidden in sticky immersive + * mode. + */ + public boolean canStartTrackpadGesture() { + return (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0 && (mSystemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING) == 0 && (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0 && (mSystemUiStateFlags & SYSUI_STATE_MAGNIFICATION_OVERLAP) == 0 @@ -607,7 +567,6 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { pw.println(" assistantAvailable=" + mAssistantAvailable); pw.println(" assistantDisabled=" + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)); - pw.println(" isUserUnlocked=" + mIsUserUnlocked); pw.println(" isOneHandedModeEnabled=" + mIsOneHandedModeEnabled); pw.println(" isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled); pw.println(" deferredGestureRegion=" + mDeferredGestureRegion.getBounds()); diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java index 8626c40ae0..2d47097671 100644 --- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java +++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java @@ -19,6 +19,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Surface.ROTATION_0; import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe; +import static com.android.launcher3.MotionEventsUtils.isTrackpadScroll; import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN; import static com.android.launcher3.util.DisplayController.CHANGE_ALL; import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE; @@ -232,16 +233,18 @@ public class RotationTouchHelper implements DisplayInfoChangeListener { /** * @return whether the coordinates of the {@param event} is in the swipe up gesture region. */ - public boolean isInSwipeUpTouchRegion(MotionEvent event, BaseActivityInterface activity) { - return isInSwipeUpTouchRegion(event, 0, activity); + public boolean isInSwipeUpTouchRegion(MotionEvent event) { + return isInSwipeUpTouchRegion(event, 0); } /** * @return whether the coordinates of the {@param event} with the given {@param pointerIndex} * is in the swipe up gesture region. */ - public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex, - BaseActivityInterface activity) { + public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex) { + if (isTrackpadScroll(event)) { + return false; + } if (isTrackpadMultiFingerSwipe(event)) { return true; } diff --git a/quickstep/src/com/android/quickstep/SplitSelectionListener.kt b/quickstep/src/com/android/quickstep/SplitSelectionListener.kt new file mode 100644 index 0000000000..5025c1c181 --- /dev/null +++ b/quickstep/src/com/android/quickstep/SplitSelectionListener.kt @@ -0,0 +1,17 @@ +package com.android.quickstep + +interface SplitSelectionListener { + /** Called when the first app has been selected with the intention to launch split screen */ + fun onSplitSelectionActive() + + /** Called when the second app has been selected with the intention to launch split screen */ + fun onSplitSelectionConfirmed() + + /** + * Called when the user no longer is in the process of selecting apps for split screen. + * [launchedSplit] will be true if selected apps have launched successfully (either in + * split screen or fullscreen), false if the user canceled/exited the selection process + */ + fun onSplitSelectionExit(launchedSplit: Boolean) { + } +}
\ No newline at end of file diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java index 25ac47a45f..e481165912 100644 --- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java +++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java @@ -15,8 +15,8 @@ */ package com.android.quickstep; -import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; -import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.ACCELERATE_1_5; +import static com.android.app.animation.Interpolators.LINEAR; import android.animation.Animator; import android.content.Context; @@ -218,7 +218,7 @@ public abstract class SwipeUpAnimationLogic implements if (progress >= end) { return 0f; } - return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5); + return Utilities.mapToRange(progress, start, end, 1, 0, ACCELERATE_1_5); } } diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 9350c729ca..fae929a7e5 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -19,6 +19,7 @@ import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import static com.android.quickstep.util.LogUtils.splitFailureMessage; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -61,6 +62,7 @@ import com.android.internal.view.AppearanceRegion; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.SplitConfigurationOptions; +import com.android.quickstep.util.AssistUtils; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.RecentsAnimationControllerCompat; @@ -74,6 +76,7 @@ import com.android.wm.shell.back.IBackAnimation; import com.android.wm.shell.bubbles.IBubbles; import com.android.wm.shell.bubbles.IBubblesListener; import com.android.wm.shell.desktopmode.IDesktopMode; +import com.android.wm.shell.desktopmode.IDesktopTaskListener; import com.android.wm.shell.draganddrop.IDragAndDrop; import com.android.wm.shell.onehanded.IOneHanded; import com.android.wm.shell.pip.IPip; @@ -82,6 +85,7 @@ import com.android.wm.shell.recents.IRecentTasks; import com.android.wm.shell.recents.IRecentTasksListener; import com.android.wm.shell.splitscreen.ISplitScreen; import com.android.wm.shell.splitscreen.ISplitScreenListener; +import com.android.wm.shell.splitscreen.ISplitSelectListener; import com.android.wm.shell.startingsurface.IStartingWindow; import com.android.wm.shell.startingsurface.IStartingWindowListener; import com.android.wm.shell.transition.IShellTransitions; @@ -126,10 +130,12 @@ public class SystemUiProxy implements ISystemUiProxy { private IPipAnimationListener mPipAnimationListener; private IBubblesListener mBubblesListener; private ISplitScreenListener mSplitScreenListener; + private ISplitSelectListener mSplitSelectListener; private IStartingWindowListener mStartingWindowListener; private ILauncherUnlockAnimationController mLauncherUnlockAnimationController; private IRecentTasksListener mRecentTasksListener; private IUnfoldTransitionListener mUnfoldAnimationListener; + private IDesktopTaskListener mDesktopTaskListener; private final LinkedHashMap<RemoteTransition, TransitionFilter> mRemoteTransitions = new LinkedHashMap<>(); private IBinder mOriginalTransactionToken = null; @@ -236,6 +242,7 @@ public class SystemUiProxy implements ISystemUiProxy { setPipAnimationListener(mPipAnimationListener); setBubblesListener(mBubblesListener); registerSplitScreenListener(mSplitScreenListener); + registerSplitSelectListener(mSplitSelectListener); setStartingWindowListener(mStartingWindowListener); setLauncherUnlockAnimationController(mLauncherUnlockAnimationController); new LinkedHashMap<>(mRemoteTransitions).forEach(this::registerRemoteTransition); @@ -243,6 +250,9 @@ public class SystemUiProxy implements ISystemUiProxy { registerRecentTasksListener(mRecentTasksListener); setBackToLauncherCallback(mBackToLauncherCallback, mBackToLauncherRunner); setUnfoldAnimationListener(mUnfoldAnimationListener); + setDesktopTaskListener(mDesktopTaskListener); + setAssistantOverridesRequested( + AssistUtils.newInstance(mContext).getSysUiAssistOverrideInvocationTypes()); } /** @@ -311,13 +321,24 @@ public class SystemUiProxy implements ISystemUiProxy { @MainThread @Override - public void onStatusBarMotionEvent(MotionEvent event) { + public void onStatusBarTouchEvent(MotionEvent event) { Preconditions.assertUIThread(); if (mSystemUiProxy != null) { try { - mSystemUiProxy.onStatusBarMotionEvent(event); + mSystemUiProxy.onStatusBarTouchEvent(event); } catch (RemoteException e) { - Log.w(TAG, "Failed call onStatusBarMotionEvent", e); + Log.w(TAG, "Failed call onStatusBarTouchEvent with arg: " + event, e); + } + } + } + + @Override + public void onStatusBarTrackpadEvent(MotionEvent event) { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.onStatusBarTrackpadEvent(event); + } catch (RemoteException e) { + Log.w(TAG, "Failed call onStatusBarTrackpadEvent with arg: " + event, e); } } } @@ -356,6 +377,17 @@ public class SystemUiProxy implements ISystemUiProxy { } @Override + public void setAssistantOverridesRequested(int[] invocationTypes) { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.setAssistantOverridesRequested(invocationTypes); + } catch (RemoteException e) { + Log.w(TAG, "Failed call setAssistantOverridesRequested", e); + } + } + } + + @Override public void notifyAccessibilityButtonClicked(int displayId) { if (mSystemUiProxy != null) { try { @@ -631,13 +663,15 @@ public class SystemUiProxy implements ISystemUiProxy { /** * Tells SysUI to show the bubble with the provided key. * @param key the key of the bubble to show. - * @param onLauncherHome whether the bubble is showing on launcher home or not (modifies where - * the expanded bubble view is placed). + * @param bubbleBarOffsetX the offset of the bubble bar from the edge of the screen on the X + * axis. + * @param bubbleBarOffsetY the offset of the bubble bar from the edge of the screen on the Y + * axis. */ - public void showBubble(String key, boolean onLauncherHome) { + public void showBubble(String key, int bubbleBarOffsetX, int bubbleBarOffsetY) { if (mBubbles != null) { try { - mBubbles.showBubble(key, onLauncherHome); + mBubbles.showBubble(key, bubbleBarOffsetX, bubbleBarOffsetY); } catch (RemoteException e) { Log.w(TAG, "Failed call showBubble"); } @@ -645,6 +679,31 @@ public class SystemUiProxy implements ISystemUiProxy { } /** + * Tells SysUI to remove the bubble with the provided key. + * @param key the key of the bubble to show. + */ + public void removeBubble(String key) { + if (mBubbles == null) return; + try { + mBubbles.removeBubble(key); + } catch (RemoteException e) { + Log.w(TAG, "Failed call removeBubble"); + } + } + + /** + * Tells SysUI to remove all bubbles. + */ + public void removeAllBubbles() { + if (mBubbles == null) return; + try { + mBubbles.removeAllBubbles(); + } catch (RemoteException e) { + Log.w(TAG, "Failed call removeAllBubbles"); + } + } + + /** * Tells SysUI to collapse the bubbles. */ public void collapseBubbles() { @@ -657,6 +716,21 @@ public class SystemUiProxy implements ISystemUiProxy { } } + /** + * Tells SysUI when the bubble is being dragged. + * Should be called only when the bubble bar is expanded. + * @param bubbleKey the key of the bubble to collapse/expand + * @param isBeingDragged whether the bubble is being dragged + */ + public void onBubbleDrag(@Nullable String bubbleKey, boolean isBeingDragged) { + if (mBubbles == null) return; + try { + mBubbles.onBubbleDrag(bubbleKey, isBeingDragged); + } catch (RemoteException e) { + Log.w(TAG, "Failed call onBubbleDrag"); + } + } + // // Splitscreen // @@ -683,6 +757,28 @@ public class SystemUiProxy implements ISystemUiProxy { mSplitScreenListener = null; } + public void registerSplitSelectListener(ISplitSelectListener listener) { + if (mSplitScreen != null) { + try { + mSplitScreen.registerSplitSelectListener(listener); + } catch (RemoteException e) { + Log.w(TAG, "Failed call registerSplitSelectListener"); + } + } + mSplitSelectListener = listener; + } + + public void unregisterSplitSelectListener(ISplitSelectListener listener) { + if (mSplitScreen != null) { + try { + mSplitScreen.unregisterSplitSelectListener(listener); + } catch (RemoteException e) { + Log.w(TAG, "Failed call unregisterSplitSelectListener"); + } + } + mSplitSelectListener = null; + } + /** Start multiple tasks in split-screen simultaneously. */ public void startTasks(int taskId1, Bundle options1, int taskId2, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio, @@ -692,7 +788,7 @@ public class SystemUiProxy implements ISystemUiProxy { mSplitScreen.startTasks(taskId1, options1, taskId2, options2, splitPosition, splitRatio, remoteTransition, instanceId); } catch (RemoteException e) { - Log.w(TAG, "Failed call startTasks"); + Log.w(TAG, splitFailureMessage("startTasks", "RemoteException"), e); } } } @@ -705,7 +801,7 @@ public class SystemUiProxy implements ISystemUiProxy { mSplitScreen.startIntentAndTask(pendingIntent, userId1, options1, taskId, options2, splitPosition, splitRatio, remoteTransition, instanceId); } catch (RemoteException e) { - Log.w(TAG, "Failed call startIntentAndTask"); + Log.w(TAG, splitFailureMessage("startIntentAndTask", "RemoteException"), e); } } } @@ -721,7 +817,7 @@ public class SystemUiProxy implements ISystemUiProxy { pendingIntent2, userId2, shortcutInfo2, options2, splitPosition, splitRatio, remoteTransition, instanceId); } catch (RemoteException e) { - Log.w(TAG, "Failed call startIntents"); + Log.w(TAG, splitFailureMessage("startIntents", "RemoteException"), e); } } } @@ -734,7 +830,7 @@ public class SystemUiProxy implements ISystemUiProxy { mSplitScreen.startShortcutAndTask(shortcutInfo, options1, taskId, options2, splitPosition, splitRatio, remoteTransition, instanceId); } catch (RemoteException e) { - Log.w(TAG, "Failed call startShortcutAndTask"); + Log.w(TAG, splitFailureMessage("startShortcutAndTask", "RemoteException"), e); } } } @@ -750,7 +846,8 @@ public class SystemUiProxy implements ISystemUiProxy { mSplitScreen.startTasksWithLegacyTransition(taskId1, options1, taskId2, options2, splitPosition, splitRatio, adapter, instanceId); } catch (RemoteException e) { - Log.w(TAG, "Failed call startTasksWithLegacyTransition"); + Log.w(TAG, splitFailureMessage( + "startTasksWithLegacyTransition", "RemoteException"), e); } } } @@ -764,7 +861,8 @@ public class SystemUiProxy implements ISystemUiProxy { mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, userId1, options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId); } catch (RemoteException e) { - Log.w(TAG, "Failed call startIntentAndTaskWithLegacyTransition"); + Log.w(TAG, splitFailureMessage( + "startIntentAndTaskWithLegacyTransition", "RemoteException"), e); } } } @@ -777,7 +875,8 @@ public class SystemUiProxy implements ISystemUiProxy { mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId); } catch (RemoteException e) { - Log.w(TAG, "Failed call startShortcutAndTaskWithLegacyTransition"); + Log.w(TAG, splitFailureMessage( + "startShortcutAndTaskWithLegacyTransition", "RemoteException"), e); } } } @@ -797,7 +896,8 @@ public class SystemUiProxy implements ISystemUiProxy { shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2, options2, sidePosition, splitRatio, adapter, instanceId); } catch (RemoteException e) { - Log.w(TAG, "Failed call startIntentsWithLegacyTransition"); + Log.w(TAG, splitFailureMessage( + "startIntentsWithLegacyTransition", "RemoteException"), e); } } } @@ -809,7 +909,7 @@ public class SystemUiProxy implements ISystemUiProxy { mSplitScreen.startShortcut(packageName, shortcutId, position, options, user, instanceId); } catch (RemoteException e) { - Log.w(TAG, "Failed call startShortcut"); + Log.w(TAG, splitFailureMessage("startShortcut", "RemoteException"), e); } } } @@ -821,7 +921,7 @@ public class SystemUiProxy implements ISystemUiProxy { mSplitScreen.startIntent(intent, userId, fillInIntent, position, options, instanceId); } catch (RemoteException e) { - Log.w(TAG, "Failed call startIntent"); + Log.w(TAG, splitFailureMessage("startIntent", "RemoteException"), e); } } } @@ -1161,6 +1261,41 @@ public class SystemUiProxy implements ISystemUiProxy { } } + /** Call shell to stash desktop apps */ + public void stashDesktopApps(int displayId) { + if (mDesktopMode != null) { + try { + mDesktopMode.stashDesktopApps(displayId); + } catch (RemoteException e) { + Log.w(TAG, "Failed call stashDesktopApps", e); + } + } + } + + /** Call shell to hide desktop apps that may be stashed */ + public void hideStashedDesktopApps(int displayId) { + if (mDesktopMode != null) { + try { + mDesktopMode.hideStashedDesktopApps(displayId); + } catch (RemoteException e) { + Log.w(TAG, "Failed call hideStashedDesktopApps", e); + } + } + } + + /** + * If task with the given id is on the desktop, bring it to front + */ + public void showDesktopApp(int taskId) { + if (mDesktopMode != null) { + try { + mDesktopMode.showDesktopApp(taskId); + } catch (RemoteException e) { + Log.w(TAG, "Failed call showDesktopApp", e); + } + } + } + /** Call shell to get number of visible freeform tasks */ public int getVisibleDesktopTaskCount(int displayId) { if (mDesktopMode != null) { @@ -1173,6 +1308,29 @@ public class SystemUiProxy implements ISystemUiProxy { return 0; } + /** Set a listener on shell to get updates about desktop task state */ + public void setDesktopTaskListener(@Nullable IDesktopTaskListener listener) { + mDesktopTaskListener = listener; + if (mDesktopMode != null) { + try { + mDesktopMode.setTaskListener(listener); + } catch (RemoteException e) { + Log.w(TAG, "Failed call setDesktopTaskListener", e); + } + } + } + + /** Perform cleanup transactions after animation to split select is complete */ + public void onDesktopSplitSelectAnimComplete(ActivityManager.RunningTaskInfo taskInfo) { + if (mDesktopMode != null) { + try { + mDesktopMode.onDesktopSplitSelectAnimComplete(taskInfo); + } catch (RemoteException e) { + Log.w(TAG, "Failed call onDesktopSplitSelectAnimComplete", e); + } + } + } + // // Unfold transition // diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index eacca0dbab..2896fe04b7 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -38,6 +38,7 @@ import androidx.annotation.UiThread; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.DisplayController; import com.android.quickstep.TopTaskTracker.CachedTaskInfo; import com.android.quickstep.util.ActiveGestureLog; @@ -61,7 +62,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn private RecentsAnimationTargets mTargets; // Temporary until we can hook into gesture state events private GestureState mLastGestureState; - private RemoteAnimationTarget mLastAppearedTaskTarget; + private RemoteAnimationTarget[] mLastAppearedTaskTargets; private Runnable mLiveTileCleanUpHandler; private Context mCtx; @@ -124,13 +125,14 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn // If mCallbacks still != null, that means we are getting this startRecentsAnimation() // before the previous one got onRecentsAnimationStart(). In that case, cleanup the // previous animation so it doesn't mess up/listen to state changes in this animation. - cleanUpRecentsAnimation(); + cleanUpRecentsAnimation(mCallbacks); } final BaseActivityInterface activityInterface = gestureState.getActivityInterface(); mLastGestureState = gestureState; - mCallbacks = new RecentsAnimationCallbacks(SystemUiProxy.INSTANCE.get(mCtx), - activityInterface.allowMinimizeSplitScreen()); + RecentsAnimationCallbacks newCallbacks = new RecentsAnimationCallbacks( + SystemUiProxy.INSTANCE.get(mCtx), activityInterface.allowMinimizeSplitScreen()); + mCallbacks = newCallbacks; mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() { @Override public void onRecentsAnimationStart(RecentsAnimationController controller, @@ -143,18 +145,25 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn } mController = controller; mTargets = targets; - mLastAppearedTaskTarget = mTargets.findTask(mLastGestureState.getRunningTaskId()); - mLastGestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget); + // TODO(b/236226779): We can probably get away w/ setting mLastAppearedTaskTargets + // to all appeared targets directly vs just looking at running ones + int[] runningTaskIds = mLastGestureState.getRunningTaskIds(targets.apps.length > 1); + mLastAppearedTaskTargets = new RemoteAnimationTarget[runningTaskIds.length]; + for (int i = 0; i < runningTaskIds.length; i++) { + RemoteAnimationTarget task = mTargets.findTask(runningTaskIds[i]); + mLastAppearedTaskTargets[i] = task; + } + mLastGestureState.updateLastAppearedTaskTargets(mLastAppearedTaskTargets); } @Override public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) { - cleanUpRecentsAnimation(); + cleanUpRecentsAnimation(newCallbacks); } @Override public void onRecentsAnimationFinished(RecentsAnimationController controller) { - cleanUpRecentsAnimation(); + cleanUpRecentsAnimation(newCallbacks); } @Override @@ -172,6 +181,9 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn RecentsView recentsView = activityInterface.getCreatedActivity().getOverviewPanel(); if (recentsView != null) { + Log.d(TestProtocol.INCORRECT_HOME_STATE, + "finish recents animation on " + + compat.taskInfo.description); recentsView.finishRecentsAnimation(true, null); } return; @@ -204,14 +216,18 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn true /*shown*/, null /* animatorHandler */); } if (mController != null) { - if (mLastAppearedTaskTarget == null - || appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) { - if (mLastAppearedTaskTarget != null) { - mController.removeTaskTarget(mLastAppearedTaskTarget); + if (mLastAppearedTaskTargets != null) { + for (RemoteAnimationTarget lastTarget : mLastAppearedTaskTargets) { + for (RemoteAnimationTarget appearedTarget : appearedTaskTargets) { + if (lastTarget != null && + appearedTarget.taskId != lastTarget.taskId) { + mController.removeTaskTarget(lastTarget.taskId); + } + } } - mLastAppearedTaskTarget = appearedTaskTarget; - mLastGestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget); } + mLastAppearedTaskTargets = appearedTaskTargets; + mLastGestureState.updateLastAppearedTaskTargets(mLastAppearedTaskTargets); } } @@ -276,7 +292,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn mCallbacks.addListener(gestureState); gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED | STATE_RECENTS_ANIMATION_STARTED); - gestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget); + gestureState.updateLastAppearedTaskTargets(mLastAppearedTaskTargets); return mCallbacks; } @@ -315,11 +331,10 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn * Finishes the running recents animation. * @param forceFinish will synchronously finish the controller */ - private void finishRunningRecentsAnimation(boolean toHome, boolean forceFinish) { + public void finishRunningRecentsAnimation(boolean toHome, boolean forceFinish) { if (mController != null) { ActiveGestureLog.INSTANCE.addLog( /* event= */ "finishRunningRecentsAnimation", toHome); - mCallbacks.notifyAnimationCanceled(); if (forceFinish) { mController.finishController(toHome, null, false /* sendUserLeaveHint */, true /* forceFinish */); @@ -328,7 +343,6 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn ? mController::finishAnimationToHome : mController::finishAnimationToApp); } - cleanUpRecentsAnimation(); } } @@ -355,7 +369,12 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn /** * Cleans up the recents animation entirely. */ - private void cleanUpRecentsAnimation() { + private void cleanUpRecentsAnimation(RecentsAnimationCallbacks targetCallbacks) { + if (mCallbacks != targetCallbacks) { + ActiveGestureLog.INSTANCE.addLog( + /* event= */ "cleanUpRecentsAnimation skipped due to wrong callbacks"); + return; + } ActiveGestureLog.INSTANCE.addLog(/* event= */ "cleanUpRecentsAnimation"); if (mLiveTileCleanUpHandler != null) { mLiveTileCleanUpHandler.run(); @@ -377,7 +396,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn mCallbacks = null; mTargets = null; mLastGestureState = null; - mLastAppearedTaskTarget = null; + mLastAppearedTaskTargets = null; } @Nullable diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java index 7c05a1092c..164a36607d 100644 --- a/quickstep/src/com/android/quickstep/TaskIconCache.java +++ b/quickstep/src/com/android/quickstep/TaskIconCache.java @@ -15,7 +15,6 @@ */ package com.android.quickstep; -import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED; import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY; import android.annotation.Nullable; @@ -182,17 +181,14 @@ public class TaskIconCache implements DisplayInfoChangeListener { } } - // Loading content descriptions if accessibility or low RAM recents is enabled. - if (GO_LOW_RAM_RECENTS_ENABLED || mAccessibilityManager.isEnabled()) { - // Skip loading the content description if the activity no longer exists - if (activityInfo == null) { - activityInfo = PackageManagerWrapper.getInstance().getActivityInfo( - key.getComponent(), key.userId); - } - if (activityInfo != null) { - entry.contentDescription = getBadgedContentDescription( - activityInfo, task.key.userId, task.taskDescription); - } + // Skip loading the content description if the activity no longer exists + if (activityInfo == null) { + activityInfo = PackageManagerWrapper.getInstance().getActivityInfo( + key.getComponent(), key.userId); + } + if (activityInfo != null) { + entry.contentDescription = getBadgedContentDescription( + activityInfo, task.key.userId, task.taskDescription); } mIconCache.put(task.key, entry); diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java index 1744b08cf5..076f4b1f01 100644 --- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java +++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java @@ -122,6 +122,12 @@ public class TaskOverlayFactory implements ResourceBasedOverride { public void removeListeners() { } + /** + * Clears any active state outside of the TaskOverlay lifecycle which might have built + * up over time + */ + public void clearAllActiveState() { } + /** Note that these will be shown in order from top to bottom, if available for the task. */ private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{ TaskShortcutFactory.APP_INFO, @@ -159,6 +165,10 @@ public class TaskOverlayFactory implements ResourceBasedOverride { return mActionsView; } + public TaskThumbnailView getThumbnailView() { + return mThumbnailView; + } + /** * Called when the current task is interactive for the user */ diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java index 5ac73512af..d82120f478 100644 --- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java +++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java @@ -140,6 +140,7 @@ public interface TaskShortcutFactory { @Override public void onClick(View view) { + dismissTaskMenuView(mTarget); ((RecentsView) mTarget.getOverviewPanel()) .getSplitSelectController().getAppPairsController().saveAppPair(mTaskView); } @@ -396,11 +397,12 @@ public interface TaskShortcutFactory { @Override public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity, TaskIdAttributeContainer taskContainer) { - return InstantAppResolver.newInstance(activity).isInstantApp(activity, - taskContainer.getTask().getTopComponent().getPackageName()) ? - Collections.singletonList(new SystemShortcut.Install(activity, - taskContainer.getItemInfo(), taskContainer.getTaskView())) : - null; + Task t = taskContainer.getTask(); + return InstantAppResolver.newInstance(activity).isInstantApp( + t.getTopComponent().getPackageName(), t.getKey().userId) + ? Collections.singletonList(new SystemShortcut.Install(activity, + taskContainer.getItemInfo(), taskContainer.getTaskView())) + : null; } }; diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java index 67360c4cc3..80a449bf5b 100644 --- a/quickstep/src/com/android/quickstep/TaskUtils.java +++ b/quickstep/src/com/android/quickstep/TaskUtils.java @@ -33,6 +33,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.pm.UserCache; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.util.TraceHelper; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -51,7 +52,8 @@ public final class TaskUtils { * TODO: remove this once we switch to getting the icon and label from IconCache. */ public static CharSequence getTitle(Context context, Task task) { - return getTitle(context, task.key.userId, task.getTopComponent().getPackageName()); + return TraceHelper.allowIpcs("TaskUtils.getTitle", () -> + getTitle(context, task.key.userId, task.getTopComponent().getPackageName())); } public static CharSequence getTitle( diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index 1238819acd..af49774061 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -21,6 +21,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.TOUCH_RESPONSE; +import static com.android.app.animation.Interpolators.clampToProgress; import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; @@ -35,9 +38,8 @@ import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DU import static com.android.launcher3.QuickstepTransitionManager.SPLIT_DIVIDER_ANIM_DURATION; import static com.android.launcher3.QuickstepTransitionManager.SPLIT_LAUNCH_DURATION; import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; -import static com.android.launcher3.anim.Interpolators.clampToProgress; +import static com.android.launcher3.testing.shared.TestProtocol.LAUNCH_SPLIT_PAIR; +import static com.android.launcher3.testing.shared.TestProtocol.testLogD; import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE; import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED; @@ -61,12 +63,12 @@ import android.window.TransitionInfo; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.statehandlers.DepthController; @@ -160,13 +162,14 @@ public final class TaskViewUtils { } public static void createRecentsWindowAnimator( - @NonNull TaskView v, boolean skipViewChanges, + @NonNull RecentsView recentsView, + @NonNull TaskView v, + boolean skipViewChanges, @NonNull RemoteAnimationTarget[] appTargets, @NonNull RemoteAnimationTarget[] wallpaperTargets, @NonNull RemoteAnimationTarget[] nonAppTargets, @Nullable DepthController depthController, PendingAnimation out) { - RecentsView recentsView = v.getRecentsView(); boolean isQuickSwitch = v.isEndQuickswitchCuj(); v.setEndQuickswitchCuj(false); @@ -237,12 +240,12 @@ public final class TaskViewUtils { for (RemoteTargetHandle targetHandle : remoteTargetHandles) { TaskViewSimulator tvsLocal = targetHandle.getTaskViewSimulator(); out.setFloat(tvsLocal.fullScreenProgress, - AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR); + AnimatedFloat.VALUE, 1, TOUCH_RESPONSE); out.setFloat(tvsLocal.recentsViewScale, AnimatedFloat.VALUE, tvsLocal.getFullScreenScale(), - TOUCH_RESPONSE_INTERPOLATOR); + TOUCH_RESPONSE); out.setFloat(tvsLocal.recentsViewScroll, AnimatedFloat.VALUE, 0, - TOUCH_RESPONSE_INTERPOLATOR); + TOUCH_RESPONSE); out.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { @@ -355,9 +358,9 @@ public final class TaskViewUtils { float fullScreenScale = topMostSimulators[i].getTaskViewSimulator().getFullScreenScale(); out.addFloat(ttv, VIEW_TRANSLATE_Y, translationY, - translationY / fullScreenScale, TOUCH_RESPONSE_INTERPOLATOR); + translationY / fullScreenScale, TOUCH_RESPONSE); out.addFloat(ttv, VIEW_TRANSLATE_X, translationX, - translationX / fullScreenScale, TOUCH_RESPONSE_INTERPOLATOR); + translationX / fullScreenScale, TOUCH_RESPONSE); } Matrix[] k0i = new Matrix[matrixSize]; @@ -405,31 +408,26 @@ public final class TaskViewUtils { if (depthController != null) { out.setFloat(depthController.stateDepth, MULTI_PROPERTY_VALUE, - BACKGROUND_APP.getDepth(baseActivity), TOUCH_RESPONSE_INTERPOLATOR); + BACKGROUND_APP.getDepth(baseActivity), TOUCH_RESPONSE); } } /** - * TODO: This doesn't animate at present. Feel free to blow out everyhing in this method - * if needed - * - * We could manually try to animate the just the bounds for the leashes we get back, but we try - * to do it through TaskViewSimulator(TVS) since that handles a lot of the recents UI stuff for - * us. - * - * First you have to call TVS#setPreview() to indicate which leash it will operate one - * Then operations happen in TVS#apply() on each frame callback. + * If {@param launchingTaskView} is not null, then this will play the tasks launch animation + * from the position of the GroupedTaskView (when user taps on the TaskView to start it). + * Technically this case should be taken care of by + * {@link #composeRecentsSplitLaunchAnimatorLegacy} below, but the way we launch tasks whether + * it's a single task or multiple tasks results in different entry-points. * - * TVS uses DeviceProfile to try to figure out things like task height and such based on if the - * device is in multiWindowMode or not. It's unclear given the two calls to startTask() when the - * device is considered in multiWindowMode and things like insets and stuff change - * and calculations have to be adjusted in the animations for that + * If it is null, then it will simply fade in the starting apps and fade out launcher (for the + * case where launcher handles animating starting split tasks from app icon) */ public static void composeRecentsSplitLaunchAnimator(GroupedTaskView launchingTaskView, @NonNull StateManager stateManager, @Nullable DepthController depthController, int initialTaskId, int secondTaskId, @NonNull TransitionInfo transitionInfo, SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { if (launchingTaskView != null) { + testLogD(LAUNCH_SPLIT_PAIR, "composeRecentsSplitLaunchAnimator taskView not-null"); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.addListener(new AnimatorListenerAdapter() { @Override @@ -457,12 +455,15 @@ public final class TaskViewUtils { return; } - // TODO: consider initialTaskPendingIntent TransitionInfo.Change splitRoot1 = null; TransitionInfo.Change splitRoot2 = null; + final ArrayList<SurfaceControl> openingTargets = new ArrayList<>(); for (int i = 0; i < transitionInfo.getChanges().size(); ++i) { final TransitionInfo.Change change = transitionInfo.getChanges().get(i); - if (change.getTaskInfo() == null) continue; + if (change.getTaskInfo() == null) { + testLogD(LAUNCH_SPLIT_PAIR, "changeTaskInfo null; change: " + change); + continue; + } final int taskId = change.getTaskInfo().taskId; final int mode = change.getMode(); @@ -477,31 +478,48 @@ public final class TaskViewUtils { if (taskId == initialTaskId) { splitRoot1 = change.getParent() == null ? change : transitionInfo.getChange(change.getParent()); + openingTargets.add(splitRoot1.getLeash()); } if (taskId == secondTaskId) { splitRoot2 = change.getParent() == null ? change : transitionInfo.getChange(change.getParent()); + openingTargets.add(splitRoot2.getLeash()); } } - // This is where we should animate the split roots. For now, though, just make them visible. - animateSplitRoot(t, splitRoot1); - animateSplitRoot(t, splitRoot2); - - // This contains the initial state (before animation), so apply this at the beginning of - // the animation. - t.apply(); + SurfaceControl.Transaction animTransaction = new SurfaceControl.Transaction(); + ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); + animator.setDuration(SPLIT_LAUNCH_DURATION); + animator.addUpdateListener(valueAnimator -> { + float progress = valueAnimator.getAnimatedFraction(); + for (SurfaceControl leash: openingTargets) { + animTransaction.setAlpha(leash, progress); + } + animTransaction.apply(); + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + for (SurfaceControl leash: openingTargets) { + animTransaction.show(leash) + .setAlpha(leash, 0.0f); + } + animTransaction.apply(); + } - // Once there is an animation, this should be called AFTER the animation completes. - finishCallback.run(); - } + @Override + public void onAnimationEnd(Animator animation) { + finishCallback.run(); + } + }); - private static void animateSplitRoot(SurfaceControl.Transaction t, - TransitionInfo.Change splitRoot) { - if (splitRoot != null) { - t.show(splitRoot.getLeash()); - t.setAlpha(splitRoot.getLeash(), 1.f); + if (splitRoot1 != null && splitRoot1.getParent() != null) { + // Set the highest level split root alpha; we could technically use the parent of either + // splitRoot1 or splitRoot2 + t.setAlpha(transitionInfo.getChange(splitRoot1.getParent()).getLeash(), 1f); } + t.apply(); + animator.start(); } /** @@ -514,7 +532,9 @@ public final class TaskViewUtils { * it's a single task or multiple tasks results in different entry-points. * * If it is null, then it will simply fade in the starting apps and fade out launcher (for the - * case where launcher handles animating starting split tasks from app icon) */ + * case where launcher handles animating starting split tasks from app icon) + * @deprecated with shell transitions + */ public static void composeRecentsSplitLaunchAnimatorLegacy( @Nullable GroupedTaskView launchingTaskView, int initialTaskId, int secondTaskId, @NonNull RemoteAnimationTarget[] appTargets, @@ -606,8 +626,8 @@ public final class TaskViewUtils { TaskView taskView = findTaskViewToLaunch(recentsView, v, appTargets); PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION); - createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets, - nonAppTargets, depthController, pa); + createRecentsWindowAnimator(recentsView, taskView, skipLauncherChanges, appTargets, + wallpaperTargets, nonAppTargets, depthController, pa); if (launcherClosing) { // TODO(b/182592057): differentiate between "restore split" vs "launch fullscreen app" TaskViewUtils.createSplitAuxiliarySurfacesAnimator(nonAppTargets, true /*shown*/, @@ -637,7 +657,7 @@ public final class TaskViewUtils { raController.setWillFinishToHome(false); } launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView); - launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR); + launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE); launcherAnim.setDuration(RECENTS_LAUNCH_DURATION); windowAnimEndListener = new AnimatorListenerAdapter() { diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java index d34cddf584..f1af2edda8 100644 --- a/quickstep/src/com/android/quickstep/TopTaskTracker.java +++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java @@ -16,7 +16,6 @@ package com.android.quickstep; import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; @@ -210,7 +209,7 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta @Nullable private final RunningTaskInfo mTopTask; - private final List<RunningTaskInfo> mAllCachedTasks; + public final List<RunningTaskInfo> mAllCachedTasks; CachedTaskInfo(List<RunningTaskInfo> allCachedTasks) { mAllCachedTasks = allCachedTasks; @@ -229,12 +228,21 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta } /** - * Returns true if the given task holds an Assistant activity that is excluded from recents + * If the given task holds an activity that is excluded from recents, and there + * is another running task that is not excluded from recents, returns that underlying task. */ - public boolean isExcludedAssistant() { - return mTopTask != null && mTopTask.configuration.windowConfiguration - .getActivityType() == ACTIVITY_TYPE_ASSISTANT - && (mTopTask.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0; + public @Nullable CachedTaskInfo otherVisibleTaskThisIsExcludedOver() { + if (mTopTask == null + || (mTopTask.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0) { + // Not an excluded task. + return null; + } + List<RunningTaskInfo> visibleNonExcludedTasks = mAllCachedTasks.stream() + .filter(t -> t.isVisible + && (t.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0) + .toList(); + return visibleNonExcludedTasks.isEmpty() ? null + : new CachedTaskInfo(visibleNonExcludedTasks); } /** diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index d6a468d39c..80682949cf 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -24,8 +24,11 @@ import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.Launcher.INTENT_ACTION_ALL_APPS_TOGGLE; +import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent; +import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe; import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH; import static com.android.quickstep.GestureState.DEFAULT_STATE; import static com.android.quickstep.GestureState.TrackpadGestureType.getTrackpadGestureType; import static com.android.quickstep.InputConsumer.TYPE_CURSOR_HOVER; @@ -37,10 +40,8 @@ import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SY import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BUBBLES; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE; @@ -56,6 +57,8 @@ import android.annotation.TargetApi; import android.app.PendingIntent; import android.app.RemoteAction; import android.app.Service; +import android.content.IIntentReceiver; +import android.content.IIntentSender; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; @@ -66,8 +69,10 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Looper; import android.os.SystemClock; +import android.os.Trace; import android.util.Log; import android.view.Choreographer; +import android.view.InputDevice; import android.view.InputEvent; import android.view.MotionEvent; import android.view.SurfaceControl; @@ -82,27 +87,26 @@ import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.R; -import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.provider.RestoreDbTask; -import com.android.launcher3.statehandlers.DesktopVisibilityController; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.launcher3.taskbar.TaskbarManager; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.ResourceUtils; import com.android.launcher3.testing.shared.TestProtocol; -import com.android.launcher3.tracing.LauncherTraceProto; -import com.android.launcher3.tracing.TouchInteractionServiceProto; import com.android.launcher3.uioverrides.flags.FlagsFactory; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.DisplayController; +import com.android.launcher3.util.LockedUserState; import com.android.launcher3.util.OnboardingPrefs; +import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.TraceHelper; import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; import com.android.quickstep.inputconsumers.AssistantInputConsumer; import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer; +import com.android.quickstep.inputconsumers.NavHandleLongPressInputConsumer; import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer; import com.android.quickstep.inputconsumers.OtherActivityInputConsumer; import com.android.quickstep.inputconsumers.OverviewInputConsumer; @@ -110,13 +114,13 @@ import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer; import com.android.quickstep.inputconsumers.ProgressDelegateInputConsumer; import com.android.quickstep.inputconsumers.ResetGestureInputConsumer; import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer; -import com.android.quickstep.inputconsumers.StatusBarInputConsumer; import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer; import com.android.quickstep.inputconsumers.TaskbarUnstashInputConsumer; +import com.android.quickstep.inputconsumers.TrackpadStatusBarInputConsumer; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.ActiveGestureLog.CompoundString; -import com.android.quickstep.util.ProtoTracer; -import com.android.quickstep.util.ProxyScreenStatusProvider; +import com.android.quickstep.util.AssistStateManager; +import com.android.quickstep.util.AssistUtils; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -124,7 +128,6 @@ import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController; -import com.android.systemui.shared.tracing.ProtoTraceable; import com.android.systemui.unfold.progress.IUnfoldAnimation; import com.android.wm.shell.back.IBackAnimation; import com.android.wm.shell.bubbles.IBubbles; @@ -140,8 +143,6 @@ import com.android.wm.shell.transition.IShellTransitions; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; -import java.util.Arrays; -import java.util.LinkedList; import java.util.function.Consumer; import java.util.function.Function; @@ -149,8 +150,7 @@ import java.util.function.Function; * Service connected by system-UI for handling touch interaction. */ @TargetApi(Build.VERSION_CODES.R) -public class TouchInteractionService extends Service - implements ProtoTraceable<LauncherTraceProto.Builder> { +public class TouchInteractionService extends Service { private static final String SUBSTRING_PREFIX = "; "; private static final String NEWLINE_PREFIX = "\n\t\t\t-> "; @@ -284,9 +284,27 @@ public class TouchInteractionService extends Service })); } + /** + * Sent when the assistant has been invoked with the given type (defined in AssistManager) + * and should be shown. This method is used if SystemUiProxy#setAssistantOverridesRequested + * was previously called including this invocation type. + */ + @Override + public void onAssistantOverrideInvoked(int invocationType) { + executeForTouchInteractionService(tis -> { + if (!AssistUtils.newInstance(tis).tryStartAssistOverride(invocationType)) { + Log.w(TAG, "Failed to invoke Assist override"); + } + }); + } + @Override public void onNavigationBarSurface(SurfaceControl surface) { // TODO: implement + if (surface != null) { + surface.release(); + surface = null; + } } @BinderThread @@ -306,24 +324,6 @@ public class TouchInteractionService extends Service @BinderThread @Override - public void onScreenTurnedOn() { - MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurnedOn); - } - - @BinderThread - @Override - public void onScreenTurningOn() { - MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurningOn); - } - - @BinderThread - @Override - public void onScreenTurningOff() { - MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurningOff); - } - - @BinderThread - @Override public void enterStageSplitFromRunningApp(boolean leftOrTop) { executeForTouchInteractionService(tis -> { StatefulActivity activity = @@ -488,11 +488,9 @@ public class TouchInteractionService extends Service BootAwarePreloader.start(this); // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized. - mDeviceState.runOnUserUnlocked(this::onUserUnlocked); - mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked); + LockedUserState.get(this).runOnUserUnlocked(this::onUserUnlocked); + LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked); mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged); - - ProtoTracer.INSTANCE.get(this).add(this); sConnected = true; } @@ -511,7 +509,7 @@ public class TouchInteractionService extends Service private void initInputMonitor(String reason) { disposeEventHandlers("Initializing input monitor due to: " + reason); - if (mDeviceState.isButtonNavMode()) { + if (mDeviceState.isButtonNavMode() && !ENABLE_TRACKPAD_GESTURE.get()) { return; } @@ -559,7 +557,7 @@ public class TouchInteractionService extends Service } private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() { - if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) { + if (!LockedUserState.get(this).isUserUnlocked() || mDeviceState.isButtonNavMode()) { // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation // mode doesn't have gestures return; @@ -579,15 +577,7 @@ public class TouchInteractionService extends Service AccessibilityManager am = getSystemService(AccessibilityManager.class); if (isHomeAndOverviewSame) { - Intent intent = new Intent(mOverviewComponentObserver.getHomeIntent()) - .setAction(INTENT_ACTION_ALL_APPS_TOGGLE); - RemoteAction allAppsAction = new RemoteAction( - Icon.createWithResource(this, R.drawable.ic_apps), - getString(R.string.all_apps_label), - getString(R.string.all_apps_label), - PendingIntent.getActivity(this, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS, intent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)); - am.registerSystemAction(allAppsAction, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); + am.registerSystemAction(createAllAppsAction(), GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); } else { am.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); } @@ -600,26 +590,43 @@ public class TouchInteractionService extends Service mTISBinder.onOverviewTargetChange(); } + private RemoteAction createAllAppsAction() { + final Intent homeIntent = new Intent(mOverviewComponentObserver.getHomeIntent()) + .setAction(INTENT_ACTION_ALL_APPS_TOGGLE); + final PendingIntent actionPendingIntent; + + if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) { + actionPendingIntent = new PendingIntent(new IIntentSender.Stub() { + @Override + public void send(int code, Intent intent, String resolvedType, + IBinder allowlistToken, IIntentReceiver finishedReceiver, + String requiredPermission, Bundle options) { + MAIN_EXECUTOR.execute(() -> mTaskbarManager.toggleAllApps(homeIntent)); + } + }); + } else { + actionPendingIntent = PendingIntent.getActivity( + this, + GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS, + homeIntent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + } + + return new RemoteAction( + Icon.createWithResource(this, R.drawable.ic_apps), + getString(R.string.all_apps_label), + getString(R.string.all_apps_label), + actionPendingIntent); + } + @UiThread private void onSystemUiFlagsChanged(int lastSysUIFlags) { - if (mDeviceState.isUserUnlocked()) { + if (LockedUserState.get(this).isUserUnlocked()) { int systemUiStateFlags = mDeviceState.getSystemUiStateFlags(); SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags); mOverviewComponentObserver.onSystemUiStateChanged(); mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags); - boolean wasFreeformActive = - (lastSysUIFlags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0; - boolean isFreeformActive = - (systemUiStateFlags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0; - if (wasFreeformActive != isFreeformActive) { - DesktopVisibilityController controller = - LauncherActivityInterface.INSTANCE.getDesktopVisibilityController(); - if (controller != null) { - controller.setFreeformTasksVisible(isFreeformActive); - } - } - int isShadeExpandedFlag = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED; boolean wasExpanded = (lastSysUIFlags & isShadeExpandedFlag) != 0; @@ -629,25 +636,12 @@ public class TouchInteractionService extends Service // overview. mTaskAnimationManager.endLiveTile(); } - - if ((lastSysUIFlags & SYSUI_STATE_TRACING_ENABLED) != - (systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED)) { - // Update the tracing state - if ((systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED) != 0) { - Log.d(TAG, "Starting tracing."); - ProtoTracer.INSTANCE.get(this).start(); - } else { - Log.d(TAG, "Stopping tracing. Dumping to file=" - + ProtoTracer.INSTANCE.get(this).getTraceFile()); - ProtoTracer.INSTANCE.get(this).stop(); - } - } } } @UiThread private void onAssistantVisibilityChanged() { - if (mDeviceState.isUserUnlocked()) { + if (LockedUserState.get(this).isUserUnlocked()) { mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged( mDeviceState.getAssistantVisibility()); } @@ -657,15 +651,13 @@ public class TouchInteractionService extends Service public void onDestroy() { Log.d(TAG, "Touch service destroyed: user=" + getUserId()); sIsInitialized = false; - if (mDeviceState.isUserUnlocked()) { + if (LockedUserState.get(this).isUserUnlocked()) { mInputConsumer.unregisterInputConsumer(); mOverviewComponentObserver.onDestroy(); } disposeEventHandlers("TouchInteractionService onDestroy()"); mDeviceState.destroy(); SystemUiProxy.INSTANCE.get(this).clearProxy(); - ProtoTracer.INSTANCE.get(this).stop(); - ProtoTracer.INSTANCE.get(this).remove(this); getSystemService(AccessibilityManager.class) .unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); @@ -691,12 +683,12 @@ public class TouchInteractionService extends Service TestLogging.recordMotionEvent( TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event); - if (!mDeviceState.isUserUnlocked()) { + if (!LockedUserState.get(this).isUserUnlocked() || (mDeviceState.isButtonNavMode() + && !isTrackpadMotionEvent(event))) { return; } - Object traceToken = TraceHelper.INSTANCE.beginFlagsOverride( - TraceHelper.FLAG_ALLOW_BINDER_TRACKING); + SafeCloseable traceToken = TraceHelper.INSTANCE.allowIpcs("TIS.onInputEvent"); final int action = event.getActionMasked(); // Note this will create a new consumer every mouse click, as after ACTION_UP from the click @@ -707,8 +699,7 @@ public class TouchInteractionService extends Service mRotationTouchHelper.setOrientationTransformIfNeeded(event); if ((!mDeviceState.isOneHandedModeActive() - && mRotationTouchHelper.isInSwipeUpTouchRegion(event, - mOverviewComponentObserver.getActivityInterface())) + && mRotationTouchHelper.isInSwipeUpTouchRegion(event)) || isHoverActionWithoutConsumer) { // Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger // onConsumerInactive and wipe the previous gesture state @@ -720,7 +711,8 @@ public class TouchInteractionService extends Service mGestureState = newGestureState; mConsumer = newConsumer(prevGestureState, mGestureState, event); mUncheckedConsumer = mConsumer; - } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode() + } else if (LockedUserState.get(this).isUserUnlocked() + && (mDeviceState.isFullyGesturalNavMode() || isTrackpadMultiFingerSwipe(event)) && mDeviceState.canTriggerAssistantAction(event)) { mGestureState = createGestureState(mGestureState, getTrackpadGestureType(event)); @@ -783,10 +775,7 @@ public class TouchInteractionService extends Service if (mGestureState.isTrackpadGesture() && (action == ACTION_POINTER_DOWN || action == ACTION_POINTER_UP)) { // Skip ACTION_POINTER_DOWN and ACTION_POINTER_UP events from trackpad. - if (action == ACTION_POINTER_DOWN) { - mGestureState.setTrackpadGestureType(getTrackpadGestureType(event)); - } - } else if (event.isHoverEvent()) { + } else if (isCursorHoverEvent(event)) { mUncheckedConsumer.onHoverEvent(event); } else { mUncheckedConsumer.onMotionEvent(event); @@ -795,8 +784,12 @@ public class TouchInteractionService extends Service if (cleanUpConsumer) { reset(); } - TraceHelper.INSTANCE.endFlagsOverride(traceToken); - ProtoTracer.INSTANCE.get(this).scheduleFrameUpdate(); + traceToken.close(); + } + + // Talkback generates hover events on touch, which we do not want to consume. + private boolean isCursorHoverEvent(MotionEvent event) { + return event.isHoverEvent() && event.getSource() == InputDevice.SOURCE_MOUSE; } private InputConsumer tryCreateAssistantInputConsumer( @@ -830,7 +823,7 @@ public class TouchInteractionService extends Service ActiveGestureLog.INSTANCE.getLogId()); taskInfo = previousGestureState.getRunningTask(); gestureState.updateRunningTask(taskInfo); - gestureState.updateLastStartedTaskId(previousGestureState.getLastStartedTaskId()); + gestureState.updateLastStartedTaskIds(previousGestureState.getLastStartedTaskIds()); gestureState.updatePreviouslyAppearedTaskIds( previousGestureState.getPreviouslyAppearedTaskIds()); } else { @@ -862,9 +855,11 @@ public class TouchInteractionService extends Service return consumer; } - boolean canStartSystemGesture = mDeviceState.canStartSystemGesture(); + boolean canStartSystemGesture = + mGestureState.isTrackpadGesture() ? mDeviceState.canStartTrackpadGesture() + : mDeviceState.canStartSystemGesture(); - if (!mDeviceState.isUserUnlocked()) { + if (!LockedUserState.get(this).isUserUnlocked()) { CompoundString reasonString = newCompoundString("device locked"); InputConsumer consumer; if (canStartSystemGesture) { @@ -897,11 +892,12 @@ public class TouchInteractionService extends Service .append(", trying to use default input consumer"); base = getDefaultInputConsumer(reasonString); } - if (mDeviceState.isGesturalNavMode()) { + if (mDeviceState.isGesturalNavMode() || newGestureState.isTrackpadGesture()) { handleOrientationSetup(base); } - if (mDeviceState.isFullyGesturalNavMode()) { - String reasonPrefix = "device is in gesture navigation mode"; + if (mDeviceState.isFullyGesturalNavMode() || newGestureState.isTrackpadGesture()) { + String reasonPrefix = "device is in gesture navigation mode or 3-button mode with a" + + " trackpad gesture"; if (mDeviceState.canTriggerAssistantAction(event)) { reasonString.append(NEWLINE_PREFIX) .append(reasonPrefix) @@ -924,8 +920,17 @@ public class TouchInteractionService extends Service .append(SUBSTRING_PREFIX) .append("TaskbarActivityContext != null, " + "using TaskbarUnstashInputConsumer"); - base = new TaskbarUnstashInputConsumer(this, base, mInputMonitorCompat, tac); + base = new TaskbarUnstashInputConsumer(this, base, mInputMonitorCompat, tac, + mOverviewCommandHelper); } + } else if (canStartSystemGesture && FeatureFlags.ENABLE_LONG_PRESS_NAV_HANDLE.get() + && !previousGestureState.isRecentsAnimationRunning()) { + reasonString.append(NEWLINE_PREFIX) + .append(reasonPrefix) + .append(SUBSTRING_PREFIX) + .append("Long press nav handle enabled, " + + "using NavHandleLongPressInputConsumer"); + base = new NavHandleLongPressInputConsumer(this, base, mInputMonitorCompat); } if (mDeviceState.isBubblesExpanded()) { @@ -945,11 +950,12 @@ public class TouchInteractionService extends Service } if (ENABLE_TRACKPAD_GESTURE.get() && mGestureState.isTrackpadGesture() - && !previousGestureState.isRecentsAnimationRunning()) { + && canStartSystemGesture && !previousGestureState.isRecentsAnimationRunning()) { reasonString = newCompoundString(reasonPrefix) .append(SUBSTRING_PREFIX) - .append("Trackpad 3-finger gesture, using StatusBarInputConsumer"); - base = new StatusBarInputConsumer(getBaseContext(), base, mInputMonitorCompat); + .append("Trackpad 3-finger gesture, using TrackpadStatusBarInputConsumer"); + base = new TrackpadStatusBarInputConsumer(getBaseContext(), base, + mInputMonitorCompat); } if (mDeviceState.isScreenPinningActive()) { @@ -1037,17 +1043,24 @@ public class TouchInteractionService extends Service } reasonString.append(SUBSTRING_PREFIX).append("keyguard is not showing occluded"); + // Use overview input consumer for sharesheets on top of home. boolean forceOverviewInputConsumer = gestureState.getActivityInterface().isStarted() && gestureState.getRunningTask() != null && gestureState.getRunningTask().isRootChooseActivity(); - if (gestureState.getRunningTask() != null - && gestureState.getRunningTask().isExcludedAssistant()) { - // In the case where we are in the excluded assistant state, ignore it and treat the - // running activity as the task behind the assistant - gestureState.updateRunningTask(TopTaskTracker.INSTANCE.get(this) - .getCachedTopTask(true /* filterOnlyVisibleRecents */)); - forceOverviewInputConsumer = gestureState.getRunningTask().isHomeTask(); + + // In the case where we are in an excluded, translucent overlay, ignore it and treat the + // running activity as the task behind the overlay. + TopTaskTracker.CachedTaskInfo otherVisibleTask = gestureState.getRunningTask() == null + ? null + : gestureState.getRunningTask().otherVisibleTaskThisIsExcludedOver(); + if (otherVisibleTask != null) { + ActiveGestureLog.INSTANCE.addLog(new CompoundString("Changing active task to ") + .append(otherVisibleTask.getPackageName()) + .append(" because the previous task running on top of this one (") + .append(gestureState.getRunningTask().getPackageName()) + .append(") was excluded from recents")); + gestureState.updateRunningTask(otherVisibleTask); } boolean previousGestureAnimatedToLauncher = @@ -1112,17 +1125,21 @@ public class TouchInteractionService extends Service private InputConsumer createDeviceLockedInputConsumer( GestureState gestureState, CompoundString reasonString) { - if (mDeviceState.isFullyGesturalNavMode() && gestureState.getRunningTask() != null) { + if ((mDeviceState.isFullyGesturalNavMode() || gestureState.isTrackpadGesture()) + && gestureState.getRunningTask() != null) { reasonString.append(SUBSTRING_PREFIX) - .append("device is in gesture nav mode and running task != null") + .append("device is in gesture nav mode or 3-button mode with a trackpad gesture" + + "and running task != null") .append(", using DeviceLockedInputConsumer"); return new DeviceLockedInputConsumer( this, mDeviceState, mTaskAnimationManager, gestureState, mInputMonitorCompat); } else { return getDefaultInputConsumer(reasonString .append(SUBSTRING_PREFIX) - .append(mDeviceState.isFullyGesturalNavMode() - ? "running task == null" : "device is not in gesture nav mode") + .append((mDeviceState.isFullyGesturalNavMode() + || gestureState.isTrackpadGesture()) + ? "running task == null" + : "device is not in gesture nav mode and it's not a trackpad gesture") .append(", trying to use default input consumer")); } } @@ -1211,11 +1228,13 @@ public class TouchInteractionService extends Service } private void preloadOverview(boolean fromInit) { + Trace.beginSection("preloadOverview(fromInit=" + fromInit + ")"); preloadOverview(fromInit, false); + Trace.endSection(); } private void preloadOverview(boolean fromInit, boolean forSUWAllSet) { - if (!mDeviceState.isUserUnlocked()) { + if (!LockedUserState.get(this).isUserUnlocked()) { return; } @@ -1251,7 +1270,7 @@ public class TouchInteractionService extends Service @Override public void onConfigurationChanged(Configuration newConfig) { - if (!mDeviceState.isUserUnlocked()) { + if (!LockedUserState.get(this).isUserUnlocked()) { return; } final BaseActivityInterface activityInterface = @@ -1261,7 +1280,10 @@ public class TouchInteractionService extends Service // We only care about the existing background activity. return; } - if (mOverviewComponentObserver.canHandleConfigChanges(activity.getComponentName(), + Configuration oldConfig = activity.getResources().getConfiguration(); + boolean isFoldUnfold = isTablet(oldConfig) != isTablet(newConfig); + if (!isFoldUnfold && mOverviewComponentObserver.canHandleConfigChanges( + activity.getComponentName(), activity.getResources().getConfiguration().diff(newConfig))) { // Since navBar gestural height are different between portrait and landscape, // can handle orientation changes and refresh navigation gestural region through @@ -1276,82 +1298,47 @@ public class TouchInteractionService extends Service preloadOverview(false /* fromInit */); } - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) { - if (rawArgs.length > 0 && Utilities.IS_DEBUG_DEVICE) { - LinkedList<String> args = new LinkedList(Arrays.asList(rawArgs)); - switch (args.pollFirst()) { - case "cmd": - if (args.peekFirst() == null) { - printAvailableCommands(pw); - } else { - onCommand(pw, args); - } - break; - } - } else { - // Dump everything - FlagsFactory.dump(pw); - if (mDeviceState.isUserUnlocked()) { - PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw); - } - mDeviceState.dump(pw); - if (mOverviewComponentObserver != null) { - mOverviewComponentObserver.dump(pw); - } - if (mOverviewCommandHelper != null) { - mOverviewCommandHelper.dump(pw); - } - if (mGestureState != null) { - mGestureState.dump(pw); - } - pw.println("Input state:"); - pw.println(" mInputMonitorCompat=" + mInputMonitorCompat); - pw.println(" mInputEventReceiver=" + mInputEventReceiver); - DisplayController.INSTANCE.get(this).dump(pw); - pw.println("TouchState:"); - BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null - : mOverviewComponentObserver.getActivityInterface().getCreatedActivity(); - boolean resumed = mOverviewComponentObserver != null - && mOverviewComponentObserver.getActivityInterface().isResumed(); - pw.println(" createdOverviewActivity=" + createdOverviewActivity); - pw.println(" resumed=" + resumed); - pw.println(" mConsumer=" + mConsumer.getName()); - ActiveGestureLog.INSTANCE.dump("", pw); - RecentsModel.INSTANCE.get(this).dump("", pw); - pw.println("ProtoTrace:"); - pw.println(" file=" + ProtoTracer.INSTANCE.get(this).getTraceFile()); - if (createdOverviewActivity != null) { - createdOverviewActivity.getDeviceProfile().dump(this, "", pw); - } - mTaskbarManager.dumpLogs("", pw); - } - } - - private void printAvailableCommands(PrintWriter pw) { - pw.println("Available commands:"); - pw.println(" clear-touch-log: Clears the touch interaction log"); - pw.println(" print-gesture-log: only prints the ActiveGestureLog dump"); + private static boolean isTablet(Configuration config) { + return config.smallestScreenWidthDp >= MIN_TABLET_WIDTH; } - private void onCommand(PrintWriter pw, LinkedList<String> args) { - String cmd = args.pollFirst(); - if (cmd == null) { - pw.println("Command missing"); - printAvailableCommands(pw); - return; - } - switch (cmd) { - case "clear-touch-log": - ActiveGestureLog.INSTANCE.clear(); - break; - case "print-gesture-log": - ActiveGestureLog.INSTANCE.dump("", pw); - break; - default: - pw.println("Command does not exist: " + cmd); - printAvailableCommands(pw); + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) { + // Dump everything + FlagsFactory.dump(pw); + if (LockedUserState.get(this).isUserUnlocked()) { + PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw); } + mDeviceState.dump(pw); + if (mOverviewComponentObserver != null) { + mOverviewComponentObserver.dump(pw); + } + if (mOverviewCommandHelper != null) { + mOverviewCommandHelper.dump(pw); + } + if (mGestureState != null) { + mGestureState.dump(pw); + } + pw.println("Input state:"); + pw.println(" mInputMonitorCompat=" + mInputMonitorCompat); + pw.println(" mInputEventReceiver=" + mInputEventReceiver); + DisplayController.INSTANCE.get(this).dump(pw); + pw.println("TouchState:"); + BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null + : mOverviewComponentObserver.getActivityInterface().getCreatedActivity(); + boolean resumed = mOverviewComponentObserver != null + && mOverviewComponentObserver.getActivityInterface().isResumed(); + pw.println(" createdOverviewActivity=" + createdOverviewActivity); + pw.println(" resumed=" + resumed); + pw.println(" mConsumer=" + mConsumer.getName()); + ActiveGestureLog.INSTANCE.dump("", pw); + RecentsModel.INSTANCE.get(this).dump("", pw); + if (createdOverviewActivity != null) { + createdOverviewActivity.getDeviceProfile().dump(this, "", pw); + } + mTaskbarManager.dumpLogs("", pw); + pw.println("AssistStateManager:"); + AssistStateManager.INSTANCE.get(this).dump(" ", pw); } private AbsSwipeUpHandler createLauncherSwipeHandler( @@ -1367,18 +1354,4 @@ public class TouchInteractionService extends Service gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(), mInputConsumer); } - - @Override - public void writeToProto(LauncherTraceProto.Builder proto) { - TouchInteractionServiceProto.Builder serviceProto = - TouchInteractionServiceProto.newBuilder(); - serviceProto.setServiceConnected(true); - - if (mOverviewComponentObserver != null) { - mOverviewComponentObserver.writeToProto(serviceProto); - } - mConsumer.writeToProto(serviceProto); - - proto.setTouchInteractionService(serviceProto); - } } diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java index 11b1ab8ec9..8a9e04e488 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java @@ -15,9 +15,9 @@ */ package com.android.quickstep.fallback; -import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; -import static com.android.launcher3.anim.Interpolators.INSTANT; -import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.FINAL_FRAME; +import static com.android.app.animation.Interpolators.INSTANT; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X; diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java index 074aedd3c7..95d88cd935 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -35,6 +35,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.statemanager.StateManager.StateListener; import com.android.launcher3.util.SplitConfigurationOptions; @@ -79,11 +80,16 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta } @Override - public void startHome(boolean animated) { + protected void handleStartHome(boolean animated) { mActivity.startHome(); AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted()); } + @Override + protected boolean canStartHomeSafely() { + return mActivity.canStartHomeSafely(); + } + /** * When starting gesture interaction from home, we add a temporary invisible tile corresponding * to the home task. This allows us to handle quick-switch similarly to a quick-switching @@ -246,7 +252,11 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta setOverviewSelectEnabled(false); } if (finalState != OVERVIEW_SPLIT_SELECT) { - resetFromSplitSelectionState(); + if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + mSplitSelectStateController.resetState(); + } else { + resetFromSplitSelectionState(); + } } if (isOverlayEnabled) { diff --git a/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java index 6a36d9fd64..ec6efcb440 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java @@ -103,8 +103,7 @@ public class AccessibilityInputConsumer extends DelegateInputConsumer { if (mState == STATE_INACTIVE) { int pointerIndex = ev.getActionIndex(); if (mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev, - pointerIndex, mGestureState.getActivityInterface()) - && mDelegate.allowInterceptByParent()) { + pointerIndex) && mDelegate.allowInterceptByParent()) { setActive(ev); mActivePointerId = ev.getPointerId(pointerIndex); @@ -153,4 +152,9 @@ public class AccessibilityInputConsumer extends DelegateInputConsumer { mDelegate.onMotionEvent(ev); } } + + @Override + protected String getDelegatorName() { + return "AccessibilityInputConsumer"; + } } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java index 162ace4965..ba012c9b64 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java @@ -42,9 +42,9 @@ import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.ViewConfiguration; +import com.android.app.animation.Interpolators; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.R; -import com.android.launcher3.anim.Interpolators; import com.android.quickstep.BaseActivityInterface; import com.android.quickstep.GestureState; import com.android.quickstep.InputConsumer; @@ -209,7 +209,7 @@ public class AssistantInputConsumer extends DelegateInputConsumer { SystemUiProxy.INSTANCE.get(mContext).onAssistantProgress(0f); } }); - animator.setInterpolator(Interpolators.DEACCEL_2); + animator.setInterpolator(Interpolators.DECELERATE_2); animator.start(); } mPassedSlop = false; @@ -278,4 +278,9 @@ public class AssistantInputConsumer extends DelegateInputConsumer { return true; } } + + @Override + protected String getDelegatorName() { + return "AssistantInputConsumer"; + } } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java index 03f8eef077..63771f0d56 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java @@ -4,8 +4,8 @@ import android.view.MotionEvent; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; -import com.android.launcher3.tracing.InputConsumerProto; import com.android.quickstep.InputConsumer; +import com.android.quickstep.util.ActiveGestureLog; import com.android.systemui.shared.system.InputMonitorCompat; public abstract class DelegateInputConsumer implements InputConsumer { @@ -43,7 +43,15 @@ public abstract class DelegateInputConsumer implements InputConsumer { mDelegate.onConsumerAboutToBeSwitched(); } + /** + * Returns the name of this DelegateInputConsumer. + */ + protected abstract String getDelegatorName(); + protected void setActive(MotionEvent ev) { + ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(getDelegatorName()) + .append(" became active")); + mState = STATE_ACTIVE; TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers"); mInputMonitor.pilferPointers(); @@ -54,9 +62,4 @@ public abstract class DelegateInputConsumer implements InputConsumer { mDelegate.onMotionEvent(event); event.recycle(); } - - @Override - public void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) { - mDelegate.writeToProtoInternal(inputConsumerProto); - } } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java index 59a9582c1f..2a355848b5 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java @@ -39,9 +39,9 @@ import android.view.MotionEvent; import android.view.RemoteAnimationTarget; import android.view.VelocityTracker; +import com.android.app.animation.Interpolators; import com.android.launcher3.R; import com.android.launcher3.anim.AnimatedFloat; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.DisplayController; @@ -153,8 +153,7 @@ public class DeviceLockedInputConsumer implements InputConsumer, if (!mThresholdCrossed) { // Cancel interaction in case of multi-touch interaction int ptrIdx = ev.getActionIndex(); - if (!mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev, ptrIdx, - mGestureState.getActivityInterface())) { + if (!mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev, ptrIdx)) { int action = ev.getAction(); ev.setAction(ACTION_CANCEL); finishTouchTracking(ev); @@ -204,7 +203,7 @@ public class DeviceLockedInputConsumer implements InputConsumer, // Animate back to fullscreen before finishing ObjectAnimator animator = mProgress.animateToValue(mProgress.value, 0); animator.setDuration(100); - animator.setInterpolator(Interpolators.ACCEL); + animator.setInterpolator(Interpolators.ACCELERATE); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java new file mode 100644 index 0000000000..7a2b3430ee --- /dev/null +++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java @@ -0,0 +1,49 @@ +/* + * 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.quickstep.inputconsumers; + +import android.content.Context; + +import androidx.annotation.Nullable; + +import com.android.launcher3.R; +import com.android.launcher3.util.ResourceBasedOverride; + +/** + * Class for extending nav handle long press behavior + */ +public class NavHandleLongPressHandler implements ResourceBasedOverride { + + /** Creates NavHandleLongPressHandler as specified by overrides */ + public static NavHandleLongPressHandler newInstance(Context context) { + return Overrides.getObject(NavHandleLongPressHandler.class, context, + R.string.nav_handle_long_press_handler_class); + } + + /** + * Called when nav handle is long pressed to get the Runnable that should be executed by the + * caller to invoke long press behavior. If null is returned that means long press couldn't be + * handled. + * <p> + * A Runnable is returned here to ensure the InputConsumer can call + * {@link android.view.InputMonitor#pilferPointers()} before invoking the long press behavior + * since pilfering can break the long press behavior. + */ + public @Nullable Runnable getLongPressRunnable() { + return null; + } +} diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java new file mode 100644 index 0000000000..f824210bd8 --- /dev/null +++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java @@ -0,0 +1,88 @@ +/* + * 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.quickstep.inputconsumers; + +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; + +import android.content.Context; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; +import android.view.MotionEvent; + +import com.android.launcher3.R; +import com.android.launcher3.util.DisplayController; +import com.android.quickstep.InputConsumer; +import com.android.systemui.shared.system.InputMonitorCompat; + +/** + * Listens for a long press + */ +public class NavHandleLongPressInputConsumer extends DelegateInputConsumer { + + private final GestureDetector mLongPressDetector; + private final NavHandleLongPressHandler mNavHandleLongPressHandler; + private final float mNavHandleWidth; + private final float mScreenWidth; + + public NavHandleLongPressInputConsumer(Context context, InputConsumer delegate, + InputMonitorCompat inputMonitor) { + super(delegate, inputMonitor); + mNavHandleWidth = context.getResources().getDimensionPixelSize( + R.dimen.navigation_home_handle_width); + mScreenWidth = DisplayController.INSTANCE.get(context).getInfo().currentSize.x; + + mNavHandleLongPressHandler = NavHandleLongPressHandler.newInstance(context); + + mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() { + @Override + public void onLongPress(MotionEvent motionEvent) { + if (isInArea(motionEvent.getRawX())) { + Runnable longPressRunnable = mNavHandleLongPressHandler.getLongPressRunnable(); + if (longPressRunnable != null) { + setActive(motionEvent); + + MAIN_EXECUTOR.getHandler().postDelayed(longPressRunnable, 50); + } + } + } + }); + } + + @Override + public int getType() { + return TYPE_NAV_HANDLE_LONG_PRESS | mDelegate.getType(); + } + + @Override + public void onMotionEvent(MotionEvent ev) { + mLongPressDetector.onTouchEvent(ev); + if (mState != STATE_ACTIVE) { + mDelegate.onMotionEvent(ev); + } + } + + protected boolean isInArea(float x) { + float areaFromMiddle = mNavHandleWidth / 2.0f; + float distFromMiddle = Math.abs(mScreenWidth / 2.0f - x); + + return distFromMiddle < areaFromMiddle; + } + + @Override + protected String getDelegatorName() { + return "NavHandleLongPressInputConsumer"; + } +} diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java index 5387c8a852..83b556dc1b 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java @@ -175,4 +175,9 @@ public class OneHandedModeInputConsumer extends DelegateInputConsumer { final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX)); return angle > ANGLE_MIN && angle < ANGLE_MAX; } + + @Override + protected String getDelegatorName() { + return "OneHandedModeInputConsumer"; + } } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index f9cd4ee1cd..7e6116720b 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -28,7 +28,6 @@ import static com.android.launcher3.PagedView.DEBUG_FAILED_QUICKSWITCH; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; -import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS; import static com.android.launcher3.util.VelocityUtils.PX_PER_MS; import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID; @@ -49,7 +48,6 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; -import com.android.launcher3.tracing.InputConsumerProto; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.TraceHelper; import com.android.quickstep.AbsSwipeUpHandler; @@ -229,8 +227,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC // Until we detect the gesture, handle events as we receive them mInputEventReceiver.setBatchingEnabled(false); - Object traceToken = TraceHelper.INSTANCE.beginSection(DOWN_EVT, - FLAG_CHECK_FOR_RACE_CONDITIONS); + TraceHelper.INSTANCE.beginSection(DOWN_EVT); mActivePointerId = ev.getPointerId(0); mDownPos.set(ev.getX(), ev.getY()); mLastPos.set(mDownPos); @@ -241,15 +238,14 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC startTouchTrackingForWindowAnimation(ev.getEventTime()); } - TraceHelper.INSTANCE.endSection(traceToken); + TraceHelper.INSTANCE.endSection(); break; } case ACTION_POINTER_DOWN: { if (!mPassedPilferInputSlop) { // Cancel interaction in case of multi-touch interaction int ptrIdx = ev.getActionIndex(); - if (!mRotationTouchHelper.isInSwipeUpTouchRegion(ev, ptrIdx, - mActivityInterface)) { + if (!mRotationTouchHelper.isInSwipeUpTouchRegion(ev, ptrIdx)) { forceCancelGesture(ev); } } @@ -282,7 +278,8 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC if (!mIsDeferredDownTarget) { // Normal gesture, ensure we pass the drag slop before we start tracking // the gesture - if (Math.abs(displacement) > mTouchSlop) { + if (mGestureState.isTrackpadGesture() || Math.abs(displacement) + > mTouchSlop) { mPassedWindowMoveSlop = true; mStartDisplacement = Math.min(displacement, -mTouchSlop); } @@ -291,8 +288,8 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC float horizontalDist = Math.abs(displacementX); float upDist = -displacement; - boolean passedSlop = squaredHypot(displacementX, displacementY) - >= mSquaredTouchSlop; + boolean passedSlop = mGestureState.isTrackpadGesture() || squaredHypot( + displacementX, displacementY) >= mSquaredTouchSlop; if (!mPassedSlopOnThisGesture && passedSlop) { mPassedSlopOnThisGesture = true; @@ -352,7 +349,8 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC mInteractionHandler.updateDisplacement(displacement - mStartDisplacement); } - if (mDeviceState.isFullyGesturalNavMode()) { + if (mDeviceState.isFullyGesturalNavMode() + || mGestureState.isTrackpadGesture()) { boolean minSwipeMet = upDist >= Math.max(mMotionPauseMinDisplacement, mInteractionHandler.getThresholdToAllowMotionPause()); mInteractionHandler.setCanSlowSwipeGoHome(minSwipeMet); @@ -417,11 +415,11 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC * the animation can still be running. */ private void finishTouchTracking(MotionEvent ev) { - Object traceToken = TraceHelper.INSTANCE.beginSection(UP_EVT, - FLAG_CHECK_FOR_RACE_CONDITIONS); + TraceHelper.INSTANCE.beginSection(UP_EVT); + boolean isCanceled = ev.getActionMasked() == ACTION_CANCEL; if (mPassedWindowMoveSlop && mInteractionHandler != null) { - if (ev.getActionMasked() == ACTION_CANCEL) { + if (isCanceled) { mInteractionHandler.onGestureCancelled(); } else { mVelocityTracker.computeCurrentVelocity(PX_PER_MS); @@ -443,8 +441,10 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC if (mActiveCallbacks != null && mInteractionHandler != null) { if (mTaskAnimationManager.isRecentsAnimationRunning()) { // The animation started, but with no movement, in this case, there will be no - // animateToProgress so we have to manually finish here. - mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */); + // animateToProgress so we have to manually finish here. In the case of + // ACTION_CANCEL, someone else may be doing something so finish synchronously. + mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */, + isCanceled /* forceFinish */); } else { // The animation hasn't started yet, so insert a replacement handler into the // callbacks which immediately finishes the animation after it starts. @@ -455,7 +455,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC onInteractionGestureFinished(); } cleanupAfterGesture(); - TraceHelper.INSTANCE.endSection(traceToken); + TraceHelper.INSTANCE.endSection(); } private void cleanupAfterGesture() { @@ -512,13 +512,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC return !mPassedPilferInputSlop; } - @Override - public void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) { - if (mInteractionHandler != null) { - mInteractionHandler.writeToProto(inputConsumerProto); - } - } - /** * A listener which just finishes the animation immediately after starting. Replaces * AbsSwipeUpHandler if the gesture itself finishes before the animation even starts. diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java index eac09ad7dc..c9c64b669c 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java @@ -15,7 +15,7 @@ */ package com.android.quickstep.inputconsumers; -import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; +import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.touch.BaseSwipeDetector.calculateDuration; import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE; import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL; diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java index fbe7fde236..28ac65b721 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java @@ -28,6 +28,7 @@ import android.graphics.PointF; import android.graphics.Rect; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; +import android.view.InputDevice; import android.view.MotionEvent; import androidx.annotation.Nullable; @@ -36,10 +37,13 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.taskbar.TaskbarActivityContext; +import com.android.launcher3.taskbar.TaskbarThresholdUtils; import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback; +import com.android.launcher3.taskbar.bubbles.BubbleControllers; import com.android.launcher3.touch.OverScroll; import com.android.launcher3.util.DisplayController; import com.android.quickstep.InputConsumer; +import com.android.quickstep.OverviewCommandHelper; import com.android.systemui.shared.system.InputMonitorCompat; /** @@ -50,6 +54,7 @@ import com.android.systemui.shared.system.InputMonitorCompat; public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { private final TaskbarActivityContext mTaskbarActivityContext; + private final OverviewCommandHelper mOverviewCommandHelper; private final GestureDetector mLongPressDetector; private final float mSquaredTouchSlop; @@ -62,6 +67,7 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { private final int mTaskbarNavThresholdY; private final boolean mIsTaskbarAllAppsOpen; private boolean mHasPassedTaskbarNavThreshold; + private boolean mIsInBubbleBarArea; private final PointF mDownPos = new PointF(); private final PointF mLastPos = new PointF(); @@ -78,16 +84,19 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { private final @Nullable TransitionCallback mTransitionCallback; public TaskbarUnstashInputConsumer(Context context, InputConsumer delegate, - InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext) { + InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext, + OverviewCommandHelper overviewCommandHelper) { super(delegate, inputMonitor); mTaskbarActivityContext = taskbarActivityContext; + mOverviewCommandHelper = overviewCommandHelper; // TODO(b/270395798): remove this when cleaning up old Persistent Taskbar code. mSquaredTouchSlop = Utilities.squaredTouchSlop(context); mScreenWidth = taskbarActivityContext.getDeviceProfile().widthPx; Resources res = context.getResources(); mUnstashArea = res.getDimensionPixelSize(R.dimen.taskbar_unstash_input_area); - mTaskbarNavThreshold = res.getDimensionPixelSize(R.dimen.taskbar_from_nav_threshold); + mTaskbarNavThreshold = TaskbarThresholdUtils.getFromNavThreshold(res, + taskbarActivityContext.getDeviceProfile()); mTaskbarNavThresholdY = taskbarActivityContext.getDeviceProfile().heightPx - mTaskbarNavThreshold; mIsTaskbarAllAppsOpen = @@ -121,7 +130,11 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { public void onMotionEvent(MotionEvent ev) { mLongPressDetector.onTouchEvent(ev); if (mState != STATE_ACTIVE) { - mDelegate.onMotionEvent(ev); + boolean isStashedTaskbarHovered = isMouseEvent(ev) + && isStashedTaskbarHovered((int) ev.getX(), (int) ev.getY()); + if (!isStashedTaskbarHovered) { + mDelegate.onMotionEvent(ev); + } // Only show the transient task bar if the touch events are on the screen. if (mTaskbarActivityContext != null && !isTrackpadMotionEvent(ev)) { @@ -136,7 +149,7 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { mHasPassedTaskbarNavThreshold = false; mTaskbarActivityContext.setAutohideSuspendFlag( FLAG_AUTOHIDE_SUSPEND_TOUCHING, true); - if (isInArea(x)) { + if (isInTaskbarArea(x)) { if (!mIsTransientTaskbar) { mLongPressDownX = x; mLongPressDownY = y; @@ -145,10 +158,12 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { mCanceledUnstashHint = false; } } - if (mTransitionCallback != null && !mIsTaskbarAllAppsOpen) { mTransitionCallback.onActionDown(); } + if (mIsTransientTaskbar && isInBubbleBarArea(x)) { + mIsInBubbleBarArea = true; + } break; case MotionEvent.ACTION_POINTER_UP: int ptrIdx = ev.getActionIndex(); @@ -185,7 +200,11 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold) { mHasPassedTaskbarNavThreshold = true; - mTaskbarActivityContext.onSwipeToUnstashTaskbar(); + if (mIsInBubbleBarArea) { + mTaskbarActivityContext.onSwipeToOpenBubblebar(); + } else { + mTaskbarActivityContext.onSwipeToUnstashTaskbar(); + } } if (dY < 0) { @@ -208,21 +227,37 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { mTransitionCallback.onActionEnd(); } mHasPassedTaskbarNavThreshold = false; + mIsInBubbleBarArea = false; + break; + case MotionEvent.ACTION_BUTTON_RELEASE: + if (isStashedTaskbarHovered) { + mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HOME); + } break; } } } } - private boolean isInArea(float x) { + private boolean isInTaskbarArea(float x) { float areaFromMiddle = mUnstashArea / 2.0f; float distFromMiddle = Math.abs(mScreenWidth / 2.0f - x); return distFromMiddle < areaFromMiddle; } + private boolean isInBubbleBarArea(float x) { + if (mTaskbarActivityContext != null && mIsTransientTaskbar) { + BubbleControllers controllers = mTaskbarActivityContext.getBubbleControllers(); + if (controllers == null) return false; + Rect bubbleBarBounds = controllers.bubbleBarViewController.getBubbleBarBounds(); + return x >= bubbleBarBounds.left && x <= bubbleBarBounds.right; + } + return false; + } + private void onLongPressDetected(MotionEvent motionEvent) { if (mTaskbarActivityContext != null - && isInArea(motionEvent.getRawX()) + && isInTaskbarArea(motionEvent.getRawX()) && !mIsTransientTaskbar) { boolean taskBarPressed = mTaskbarActivityContext.onLongPressToUnstashTaskbar(); if (taskBarPressed) { @@ -255,19 +290,17 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { private void updateHoveredTaskbarState(int x, int y) { DeviceProfile dp = mTaskbarActivityContext.getDeviceProfile(); - mStashedTaskbarHandleBounds.set( + mBottomEdgeBounds.set( (dp.widthPx - (int) mUnstashArea) / 2, - dp.heightPx - dp.stashedTaskbarHeight, + dp.heightPx - mStashedTaskbarBottomEdge, (int) (((dp.widthPx - mUnstashArea) / 2) + mUnstashArea), dp.heightPx); - mBottomEdgeBounds.set(mStashedTaskbarHandleBounds); - mBottomEdgeBounds.top = dp.heightPx - mStashedTaskbarBottomEdge; if (mBottomEdgeBounds.contains(x, y)) { // If hovering stashed taskbar and then hover screen bottom edge, unstash it. mTaskbarActivityContext.onSwipeToUnstashTaskbar(); mIsStashedTaskbarHovered = false; - } else if (!mStashedTaskbarHandleBounds.contains(x, y)) { + } else if (!isStashedTaskbarHovered(x, y)) { // If exit hovering stashed taskbar, remove hint. startStashedTaskbarHover(/* isHovered = */ false); } @@ -275,18 +308,13 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { private void updateUnhoveredTaskbarState(int x, int y) { DeviceProfile dp = mTaskbarActivityContext.getDeviceProfile(); - mStashedTaskbarHandleBounds.set( - (dp.widthPx - (int) mUnstashArea) / 2, - dp.heightPx - dp.stashedTaskbarHeight, - (int) (((dp.widthPx - mUnstashArea) / 2) + mUnstashArea), - dp.heightPx); mBottomEdgeBounds.set( 0, dp.heightPx - mBottomScreenEdge, dp.widthPx, dp.heightPx); - if (mStashedTaskbarHandleBounds.contains(x, y)) { + if (isStashedTaskbarHovered(x, y)) { // If enter hovering stashed taskbar, start hint. startStashedTaskbarHover(/* isHovered = */ true); } else if (mBottomEdgeBounds.contains(x, y)) { @@ -299,4 +327,28 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { mTaskbarActivityContext.startTaskbarUnstashHint(isHovered, /* forceUnstash = */ true); mIsStashedTaskbarHovered = isHovered; } + + private boolean isStashedTaskbarHovered(int x, int y) { + if (!mTaskbarActivityContext.isTaskbarStashed() + || mTaskbarActivityContext.isTaskbarAllAppsOpen() + || !ENABLE_CURSOR_HOVER_STATES.get()) { + return false; + } + DeviceProfile dp = mTaskbarActivityContext.getDeviceProfile(); + mStashedTaskbarHandleBounds.set( + (dp.widthPx - (int) mUnstashArea) / 2, + dp.heightPx - dp.stashedTaskbarHeight, + (int) (((dp.widthPx - mUnstashArea) / 2) + mUnstashArea), + dp.heightPx); + return mStashedTaskbarHandleBounds.contains(x, y); + } + + private boolean isMouseEvent(MotionEvent event) { + return event.getSource() == InputDevice.SOURCE_MOUSE; + } + + @Override + protected String getDelegatorName() { + return "TaskbarUnstashInputConsumer"; + } } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/StatusBarInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TrackpadStatusBarInputConsumer.java index 898aa8640d..f3e21e1399 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/StatusBarInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/TrackpadStatusBarInputConsumer.java @@ -27,15 +27,15 @@ import com.android.quickstep.InputConsumer; import com.android.quickstep.SystemUiProxy; import com.android.systemui.shared.system.InputMonitorCompat; -/** Allows the status bar to be pull down for notification shade */ -public class StatusBarInputConsumer extends DelegateInputConsumer { +/** Allows the status bar to be pull down for notification shade using the trackpad. */ +public class TrackpadStatusBarInputConsumer extends DelegateInputConsumer { private final SystemUiProxy mSystemUiProxy; private final float mTouchSlop; private final PointF mDown = new PointF(); private boolean mHasPassedTouchSlop; - public StatusBarInputConsumer(Context context, InputConsumer delegate, + public TrackpadStatusBarInputConsumer(Context context, InputConsumer delegate, InputMonitorCompat inputMonitor) { super(delegate, inputMonitor); @@ -79,7 +79,12 @@ public class StatusBarInputConsumer extends DelegateInputConsumer { private void dispatchTouchEvent(MotionEvent ev) { if (mSystemUiProxy.isActive()) { - mSystemUiProxy.onStatusBarMotionEvent(ev); + mSystemUiProxy.onStatusBarTrackpadEvent(ev); } } + + @Override + protected String getDelegatorName() { + return "TrackpadStatusBarInputConsumer"; + } } diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java index 6619dd86f6..49814df57b 100644 --- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java +++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java @@ -15,10 +15,10 @@ */ package com.android.quickstep.interaction; +import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.Utilities.mapBoundToRange; import static com.android.launcher3.Utilities.mapRange; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely; import android.animation.Animator; @@ -95,11 +95,10 @@ public class AllSetActivity extends Activity { private static final float ANIMATION_PAUSE_ALPHA_THRESHOLD = 0.1f; + private final AnimatedFloat mSwipeProgress = new AnimatedFloat(this::onSwipeProgressUpdate); + private TISBindHelper mTISBindHelper; - private TISBinder mBinder; - @Nullable private TaskbarManager mTaskbarManager = null; - private final AnimatedFloat mSwipeProgress = new AnimatedFloat(this::onSwipeProgressUpdate); private BgDrawable mBackground; private View mRootView; private float mSwipeUpShift; @@ -174,7 +173,7 @@ public class AllSetActivity extends Activity { LOTTIE_TERTIARY_COLOR_TOKEN, R.color.all_set_bg_tertiary), getTheme()); - startBackgroundAnimation(); + startBackgroundAnimation(dp.isTablet); } private void runOnUiHelperThread(Runnable runnable) { @@ -185,7 +184,7 @@ public class AllSetActivity extends Activity { Executors.UI_HELPER_EXECUTOR.execute(runnable); } - private void startBackgroundAnimation() { + private void startBackgroundAnimation(boolean forTablet) { if (!Utilities.ATLEAST_S || mVibrator == null) { return; } @@ -201,7 +200,7 @@ public class AllSetActivity extends Activity { .addPrimitive(supportsThud ? VibrationEffect.Composition.PRIMITIVE_THUD : VibrationEffect.Composition.PRIMITIVE_TICK, - /* scale= */ 1.0f, + /* scale= */ forTablet ? 1.0f : 0.3f, /* delay= */ 50) .compose(); @@ -233,29 +232,30 @@ public class AllSetActivity extends Activity { } private void setSetupUIVisible(boolean visible) { - if (mBinder == null || mTaskbarManager == null) return; - mTaskbarManager.setSetupUIVisible(visible); + TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager(); + if (taskbarManager == null) return; + taskbarManager.setSetupUIVisible(visible); } @Override protected void onResume() { super.onResume(); maybeResumeOrPauseBackgroundAnimation(); - if (mBinder != null) { + TISBinder binder = mTISBindHelper.getBinder(); + if (binder != null) { setSetupUIVisible(true); - mBinder.setSwipeUpProxy(this::createSwipeUpProxy); + binder.setSwipeUpProxy(this::createSwipeUpProxy); } } private void onTISConnected(TISBinder binder) { - mBinder = binder; - mTaskbarManager = mBinder.getTaskbarManager(); setSetupUIVisible(isResumed()); - mBinder.setSwipeUpProxy(isResumed() ? this::createSwipeUpProxy : null); - mBinder.setOverviewTargetChangeListener(mBinder::preloadOverviewForSUWAllSet); - mBinder.preloadOverviewForSUWAllSet(); - if (mTaskbarManager != null) { - mLauncherStartAnim = mTaskbarManager.createLauncherStartFromSuwAnim(MAX_SWIPE_DURATION); + binder.setSwipeUpProxy(isResumed() ? this::createSwipeUpProxy : null); + binder.setOverviewTargetChangeListener(binder::preloadOverviewForSUWAllSet); + binder.preloadOverviewForSUWAllSet(); + TaskbarManager taskbarManager = binder.getTaskbarManager(); + if (taskbarManager != null) { + mLauncherStartAnim = taskbarManager.createLauncherStartFromSuwAnim(MAX_SWIPE_DURATION); } } @@ -271,10 +271,11 @@ public class AllSetActivity extends Activity { } private void clearBinderOverride() { - if (mBinder != null) { + TISBinder binder = mTISBindHelper.getBinder(); + if (binder != null) { setSetupUIVisible(false); - mBinder.setSwipeUpProxy(null); - mBinder.setOverviewTargetChangeListener(null); + binder.setSwipeUpProxy(null); + binder.setOverviewTargetChangeListener(null); } } @@ -302,7 +303,7 @@ public class AllSetActivity extends Activity { } private AnimatedFloat createSwipeUpProxy(GestureState state) { - if (state.getRunningTaskId() != getTaskId()) { + if (state.getTopRunningTaskId() != getTaskId()) { return null; } mSwipeProgress.updateValue(0); diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java index 5d2527973e..631cff7639 100644 --- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java @@ -23,9 +23,9 @@ import android.annotation.LayoutRes; import android.graphics.PointF; import android.view.View; +import com.android.app.animation.Interpolators; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.anim.Interpolators; import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult; import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult; import com.android.quickstep.util.LottieAnimationColorUtils; @@ -154,7 +154,7 @@ final class BackGestureTutorialController extends TutorialController { @Override public void onBackGestureAttempted(BackGestureResult result) { - if (skipGestureAttempt()) { + if (isGestureCompleted()) { return; } switch (mTutorialType) { @@ -172,7 +172,7 @@ final class BackGestureTutorialController extends TutorialController { @Override public void onBackGestureProgress(float diffx, float diffy, boolean isLeftGesture) { - if (skipGestureAttempt()) { + if (isGestureCompleted()) { return; } @@ -183,7 +183,7 @@ final class BackGestureTutorialController extends TutorialController { /* upperBound = */ 1f, /* toMin = */ 1f, /* toMax = */ EXITING_APP_MIN_SIZE_PERCENTAGE, - Interpolators.DEACCEL); + Interpolators.DECELERATE); // shrink the exiting app as we progress through the back gesture mExitingAppView.setPivotX(isLeftGesture ? mScreenWidth : 0); @@ -197,7 +197,7 @@ final class BackGestureTutorialController extends TutorialController { /* upperBound = */ 1f, /* toMin = */ 0, /* toMax = */ mExitingAppMargin, - Interpolators.DEACCEL) + Interpolators.DECELERATE) * (isLeftGesture ? -1 : 1)); // round the corners of the exiting app as we progress through the back gesture @@ -241,7 +241,7 @@ final class BackGestureTutorialController extends TutorialController { @Override public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) { - if (skipGestureAttempt()) { + if (isGestureCompleted()) { return; } if (mTutorialType == BACK_NAVIGATION_COMPLETE) { diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java index a16b239ff5..b379baabe0 100644 --- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java +++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java @@ -42,6 +42,12 @@ public class BackGestureTutorialFragment extends TutorialFragment { super(fromTutorialMenu); } + @NonNull + @Override + TutorialType getDefaultTutorialType() { + return TutorialType.BACK_NAVIGATION; + } + @Nullable @Override Integer getEdgeAnimationResId() { diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java index 8eb40593c9..a9dcad8db1 100644 --- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java +++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java @@ -40,8 +40,8 @@ import androidx.dynamicanimation.animation.FloatPropertyCompat; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; +import com.android.app.animation.Interpolators; import com.android.launcher3.R; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.testing.shared.ResourceUtils; import com.android.launcher3.util.VibratorWrapper; diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java index 62726a01d2..d10250224d 100644 --- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java +++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java @@ -53,18 +53,15 @@ public class GestureSandboxActivity extends FragmentActivity { static final String KEY_USE_TUTORIAL_MENU = "use_tutorial_menu"; @Nullable private TutorialType[] mTutorialSteps; - private GestureSandboxFragment mFragment; + private GestureSandboxFragment mCurrentFragment; + private GestureSandboxFragment mPendingFragment; private int mCurrentStep; private int mNumSteps; - private boolean mShowRotationPrompt; private SharedPreferences mSharedPrefs; private StatsLogManager mStatsLogManager; - - private View mRotationPrompt; private TISBindHelper mTISBindHelper; - private TISBinder mBinder; @Override protected void onCreate(Bundle savedInstanceState) { @@ -83,7 +80,7 @@ public class GestureSandboxActivity extends FragmentActivity { && args.getBoolean(KEY_USE_TUTORIAL_MENU, false)) { mTutorialSteps = null; TutorialType tutorialTypeOverride = (TutorialType) args.get(KEY_TUTORIAL_TYPE); - mFragment = tutorialTypeOverride == null + mCurrentFragment = tutorialTypeOverride == null ? new MenuFragment() : makeTutorialFragment( tutorialTypeOverride, @@ -91,16 +88,15 @@ public class GestureSandboxActivity extends FragmentActivity { /* fromMenu= */ true); } else { mTutorialSteps = getTutorialSteps(args); - mFragment = makeTutorialFragment( + mCurrentFragment = makeTutorialFragment( mTutorialSteps[mCurrentStep - 1], gestureComplete, /* fromMenu= */ false); } getSupportFragmentManager().beginTransaction() - .add(R.id.gesture_tutorial_fragment_container, mFragment) + .add(R.id.gesture_tutorial_fragment_container, mCurrentFragment) .commit(); - mRotationPrompt = findViewById(R.id.rotation_prompt); if (FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) { correctUserOrientation(); } @@ -128,34 +124,45 @@ public class GestureSandboxActivity extends FragmentActivity { DeviceProfile deviceProfile = InvariantDeviceProfile.INSTANCE.get( getApplicationContext()).getDeviceProfile(this); if (deviceProfile.isTablet) { - mShowRotationPrompt = getResources().getConfiguration().orientation + boolean showRotationPrompt = getResources().getConfiguration().orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; - updateVisibility(mRotationPrompt, mShowRotationPrompt ? View.VISIBLE : View.GONE); + + GestureSandboxFragment recreatedFragment = + showRotationPrompt || mPendingFragment == null + ? null : mPendingFragment.recreateFragment(); + showFragment(showRotationPrompt + ? new RotationPromptFragment() + : recreatedFragment == null + ? mCurrentFragment : recreatedFragment); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } } - void updateVisibility(View view, int visibility) { - if (view == null || view.getVisibility() == visibility) { - return; + private void showFragment(@NonNull GestureSandboxFragment fragment) { + if (mCurrentFragment.recreateFragment() != null) { + mPendingFragment = mCurrentFragment; } - view.setVisibility(visibility); + mCurrentFragment = fragment; + getSupportFragmentManager().beginTransaction() + .replace(R.id.gesture_tutorial_fragment_container, mCurrentFragment) + .runOnCommit(() -> mCurrentFragment.onAttachedToWindow()) + .commit(); } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); - if (mFragment.shouldDisableSystemGestures()) { + if (mCurrentFragment.shouldDisableSystemGestures()) { disableSystemGestures(); } - mFragment.onAttachedToWindow(); + mCurrentFragment.onAttachedToWindow(); } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); - mFragment.onDetachedFromWindow(); + mCurrentFragment.onDetachedFromWindow(); } @Override @@ -170,14 +177,10 @@ public class GestureSandboxActivity extends FragmentActivity { protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) { savedInstanceState.putStringArray(KEY_TUTORIAL_STEPS, getTutorialStepNames()); savedInstanceState.putInt(KEY_CURRENT_STEP, mCurrentStep); - mFragment.onSaveInstanceState(savedInstanceState); + mCurrentFragment.onSaveInstanceState(savedInstanceState); super.onSaveInstanceState(savedInstanceState); } - protected boolean isRotationPromptShowing() { - return mShowRotationPrompt; - } - protected SharedPreferences getSharedPrefs() { return mSharedPrefs; } @@ -206,7 +209,7 @@ public class GestureSandboxActivity extends FragmentActivity { */ public void continueTutorial() { if (isTutorialComplete() || mTutorialSteps == null) { - mFragment.close(); + mCurrentFragment.close(); return; } launchTutorialStep(mTutorialSteps[mCurrentStep], false); @@ -225,20 +228,12 @@ public class GestureSandboxActivity extends FragmentActivity { * the menu when complete. */ public void launchTutorialStep(@NonNull TutorialType tutorialType, boolean fromMenu) { - mFragment = makeTutorialFragment(tutorialType, false, fromMenu); - getSupportFragmentManager().beginTransaction() - .replace(R.id.gesture_tutorial_fragment_container, mFragment) - .runOnCommit(() -> mFragment.onAttachedToWindow()) - .commit(); + showFragment(makeTutorialFragment(tutorialType, false, fromMenu)); } /** Launches the gesture nav tutorial menu page */ public void launchTutorialMenu() { - mFragment = new MenuFragment(); - getSupportFragmentManager().beginTransaction() - .replace(R.id.gesture_tutorial_fragment_container, mFragment) - .runOnCommit(() -> mFragment.onAttachedToWindow()) - .commit(); + showFragment(new MenuFragment()); } private String[] getTutorialStepNames() { @@ -321,7 +316,6 @@ public class GestureSandboxActivity extends FragmentActivity { } private void onTISConnected(TISBinder binder) { - mBinder = binder; updateServiceState(isResumed()); } @@ -332,8 +326,9 @@ public class GestureSandboxActivity extends FragmentActivity { } private void updateServiceState(boolean isEnabled) { - if (mBinder != null) { - mBinder.setGestureBlockedTaskId(isEnabled ? getTaskId() : -1); + TISBinder binder = mTISBindHelper.getBinder(); + if (binder != null) { + binder.setGestureBlockedTaskId(isEnabled ? getTaskId() : -1); } } diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxFragment.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxFragment.java index d52f19a2b5..08f298965c 100644 --- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxFragment.java +++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxFragment.java @@ -17,6 +17,7 @@ package com.android.quickstep.interaction; import android.app.Activity; +import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; @@ -27,6 +28,11 @@ public abstract class GestureSandboxFragment extends Fragment { void onDetachedFromWindow() {} + @Nullable + GestureSandboxFragment recreateFragment() { + return null; + } + boolean shouldDisableSystemGestures() { return true; } diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java index 0aa80d3e79..daac99bfee 100644 --- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java @@ -147,7 +147,7 @@ final class HomeGestureTutorialController extends SwipeUpGestureTutorialControll @Override public void onBackGestureAttempted(BackGestureResult result) { - if (skipGestureAttempt()) { + if (isGestureCompleted()) { return; } switch (mTutorialType) { @@ -174,7 +174,7 @@ final class HomeGestureTutorialController extends SwipeUpGestureTutorialControll @Override public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) { - if (skipGestureAttempt()) { + if (isGestureCompleted()) { return; } switch (mTutorialType) { diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java index bced8c4aef..3e924d7ed1 100644 --- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java +++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java @@ -41,6 +41,12 @@ public class HomeGestureTutorialFragment extends TutorialFragment { super(fromTutorialMenu); } + @NonNull + @Override + TutorialType getDefaultTutorialType() { + return TutorialType.HOME_NAVIGATION; + } + @Nullable @Override Integer getEdgeAnimationResId() { diff --git a/quickstep/src/com/android/quickstep/interaction/MenuFragment.java b/quickstep/src/com/android/quickstep/interaction/MenuFragment.java index 46f79b1ce8..dbf141b987 100644 --- a/quickstep/src/com/android/quickstep/interaction/MenuFragment.java +++ b/quickstep/src/com/android/quickstep/interaction/MenuFragment.java @@ -19,7 +19,6 @@ import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_GESTU import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_TUTORIAL_TYPE; import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_USE_TUTORIAL_MENU; -import android.graphics.Rect; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -28,33 +27,23 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.R; /** Displays the gesture nav tutorial menu. */ public final class MenuFragment extends GestureSandboxFragment { - @NonNull private Rect mInsets = new Rect(); - + @NonNull @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mInsets = InvariantDeviceProfile.INSTANCE.get(getContext()) - .getDeviceProfile(getContext()).getInsets(); + GestureSandboxFragment recreateFragment() { + return new MenuFragment(); } @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View root = inflater.inflate( + final View root = inflater.inflate( R.layout.gesture_tutorial_step_menu, container, false); - root.setPadding( - root.getPaddingLeft() + mInsets.left, - root.getPaddingTop() + mInsets.top, - root.getPaddingRight() + mInsets.right, - root.getPaddingBottom() + mInsets.bottom); - root.findViewById(R.id.gesture_tutorial_menu_home_button).setOnClickListener( v -> launchTutorialStep(TutorialController.TutorialType.HOME_NAVIGATION)); root.findViewById(R.id.gesture_tutorial_menu_back_button).setOnClickListener( diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java index 6cee6906d4..c4a2216251 100644 --- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java +++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java @@ -60,12 +60,11 @@ public class NavBarGestureHandler implements OnTouchListener, NavBarGestureHandler(Context context) { mContext = context; DisplayController.Info displayInfo = DisplayController.INSTANCE.get(mContext).getInfo(); - final int displayRotation = displayInfo.rotation; Point currentSize = displayInfo.currentSize; mDisplaySize.set(currentSize.x, currentSize.y); mSwipeUpTouchTracker = new TriggerSwipeUpTouchTracker(context, true /*disableHorizontalSwipe*/, - new NavBarPosition(NavigationMode.NO_BUTTON, displayRotation), + new NavBarPosition(NavigationMode.NO_BUTTON, displayInfo), null /*onInterceptTouch*/, this); mMotionPauseDetector = new MotionPauseDetector(context); diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java index 454dd17f37..afdc1e54a7 100644 --- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java @@ -15,7 +15,7 @@ */ package com.android.quickstep.interaction; -import static com.android.launcher3.anim.Interpolators.ACCEL; +import static com.android.app.animation.Interpolators.ACCELERATE; import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL; import android.animation.Animator; @@ -179,7 +179,7 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont @Override public void onBackGestureAttempted(BackGestureResult result) { - if (skipGestureAttempt()) { + if (isGestureCompleted()) { return; } switch (mTutorialType) { @@ -206,7 +206,7 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont @Override public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) { - if (skipGestureAttempt()) { + if (isGestureCompleted()) { return; } switch (mTutorialType) { @@ -254,7 +254,7 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont public void animateTaskViewToOverview(boolean animateDelayedSuccessFeedback) { PendingAnimation anim = new PendingAnimation(TASK_VIEW_END_ANIMATION_DURATION_MILLIS); anim.setFloat(mTaskViewSwipeUpAnimation - .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL); + .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCELERATE); if (animateDelayedSuccessFeedback) { anim.addListener(new AnimatorListenerAdapter() { diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java index 01074dd276..ee1c46057e 100644 --- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java +++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java @@ -41,6 +41,12 @@ public class OverviewGestureTutorialFragment extends TutorialFragment { super(fromTutorialMenu); } + @NonNull + @Override + TutorialType getDefaultTutorialType() { + return TutorialType.OVERVIEW_NAVIGATION; + } + @Nullable @Override Integer getEdgeAnimationResId() { diff --git a/quickstep/src/com/android/quickstep/interaction/RotationPromptFragment.java b/quickstep/src/com/android/quickstep/interaction/RotationPromptFragment.java new file mode 100644 index 0000000000..89af647985 --- /dev/null +++ b/quickstep/src/com/android/quickstep/interaction/RotationPromptFragment.java @@ -0,0 +1,37 @@ +/* + * 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.quickstep.interaction; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.R; + +/** Displays the prompt requesting that the user rotates their device. */ +public class RotationPromptFragment extends GestureSandboxFragment { + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.gesture_tutorial_rotation_prompt, container, false); + } +} diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java index 558d5dc238..87defc5252 100644 --- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java @@ -18,7 +18,7 @@ package com.android.quickstep.interaction; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; -import static com.android.launcher3.anim.Interpolators.ACCEL; +import static com.android.app.animation.Interpolators.ACCELERATE; import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs; import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; import static com.android.quickstep.AbsSwipeUpHandler.MAX_SWIPE_DURATION; @@ -171,14 +171,14 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { PendingAnimation anim = new PendingAnimation(300); if (toOverviewFirst) { anim.setFloat(mTaskViewSwipeUpAnimation - .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL); + .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCELERATE); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation, boolean isReverse) { PendingAnimation fadeAnim = new PendingAnimation(TASK_VIEW_END_ANIMATION_DURATION_MILLIS); fadeAnim.setFloat(mTaskViewSwipeUpAnimation - .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCEL); + .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCELERATE); if (resetViews) { fadeAnim.addListener(mResetTaskView); } @@ -213,7 +213,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { }); } else { anim.setFloat(mTaskViewSwipeUpAnimation - .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCEL); + .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCELERATE); if (resetViews) { anim.addListener(mResetTaskView); } @@ -239,8 +239,8 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { mFakeTaskView.setVisibility(View.VISIBLE); PendingAnimation anim = new PendingAnimation(300); anim.setFloat(mTaskViewSwipeUpAnimation - .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCEL); - anim.setViewAlpha(mFakeTaskView, 1, ACCEL); + .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCELERATE); + anim.setViewAlpha(mFakeTaskView, 1, ACCELERATE); anim.addListener(mResetTaskView); AnimatorSet animset = anim.buildAnim(); if (animateTaskbar) { @@ -260,7 +260,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { mTaskViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity); // After home animation finishes, fade out and run onEndRunnable. PendingAnimation fadeAnim = new PendingAnimation(300); - fadeAnim.setViewAlpha(mFakeIconView, 0, ACCEL); + fadeAnim.setViewAlpha(mFakeIconView, 0, ACCELERATE); final View hotseatIconView = mHotseatIconView; if (hotseatIconView != null) { hotseatIconView.setVisibility(INVISIBLE); @@ -282,7 +282,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { @Override public void setNavBarGestureProgress(@Nullable Float displacement) { - if (skipGestureAttempt()) { + if (isGestureCompleted()) { return; } if (mTutorialType == HOME_NAVIGATION_COMPLETE @@ -303,7 +303,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { @Override public void onMotionPaused(boolean unused) { - if (skipGestureAttempt()) { + if (isGestureCompleted()) { return; } if (mShowTasks) { diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java index 4a1fec3ecf..545a94d641 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java @@ -226,13 +226,11 @@ abstract class TutorialController implements BackGestureAttemptCallback, return; } Matrix scaleMatrix = new Matrix(); - float pivotX = mScreenWidth / 2f; - float pivotY = mScreenHeight; float scaleFactor = mScreenWidth / animationBoundsRect.width(); + float heightTranslate = (mScreenHeight - (scaleFactor * animationBoundsRect.height())); - scaleMatrix.postScale(scaleFactor, scaleFactor, pivotX, pivotY); - scaleMatrix.postTranslate(0, - mTutorialFragment.getDeviceProfile().heightPx - animationBoundsRect.height()); + scaleMatrix.postScale(scaleFactor, scaleFactor); + scaleMatrix.postTranslate(0, heightTranslate); mAnimatedGestureDemonstration.setImageMatrix(scaleMatrix); } @@ -489,10 +487,6 @@ abstract class TutorialController implements BackGestureAttemptCallback, return mGestureCompleted; } - public boolean skipGestureAttempt() { - return isGestureCompleted() || mTutorialFragment.isRotationPromptShowing(); - } - void hideFeedback() { if (mFeedbackView.getVisibility() != View.VISIBLE) { return; diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java index 84326f5a27..a28cf58c35 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java @@ -100,6 +100,19 @@ abstract class TutorialFragment extends GestureSandboxFragment implements OnTouc return fragment; } + @Nullable + @Override + GestureSandboxFragment recreateFragment() { + TutorialType tutorialType = mTutorialController == null + ? (mTutorialType == null + ? getDefaultTutorialType() : mTutorialType) + : mTutorialController.mTutorialType; + return newInstance(tutorialType, isGestureComplete(), mFromTutorialMenu); + } + + @NonNull + abstract TutorialType getDefaultTutorialType(); + TutorialFragment(boolean fromTutorialMenu) { mFromTutorialMenu = fromTutorialMenu; } @@ -498,11 +511,6 @@ abstract class TutorialFragment extends GestureSandboxFragment implements OnTouc return activity != null ? activity.getStatsLogManager() : null; } - protected boolean isRotationPromptShowing() { - GestureSandboxActivity activity = getGestureSandboxActivity(); - return activity != null && activity.isRotationPromptShowing(); - } - @Nullable private SharedPreferences getSharedPreferences() { GestureSandboxActivity activity = getGestureSandboxActivity(); diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java index 6288937912..b1daac422a 100644 --- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java +++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java @@ -62,14 +62,11 @@ import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.util.Executors; -import com.android.launcher3.util.IntArray; import com.android.launcher3.util.LogConfig; import com.android.launcher3.views.ActivityContext; import com.android.systemui.shared.system.InteractionJankMonitorWrapper; import com.android.systemui.shared.system.SysUiStatsLog; -import java.util.ArrayList; -import java.util.List; import java.util.Optional; import java.util.OptionalInt; import java.util.concurrent.CopyOnWriteArrayList; @@ -90,6 +87,7 @@ public class StatsLogCompatManager extends StatsLogManager { private static final String LATENCY_TAG = "StatsLatencyLog"; private static final String IMPRESSION_TAG = "StatsImpressionLog"; private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG); + private static final boolean DEBUG = !Utilities.isRunningInTestHarness(); private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0); // LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates // from nano to lite, bake constant to prevent robo test failure. @@ -110,8 +108,6 @@ public class StatsLogCompatManager extends StatsLogManager { public static final CopyOnWriteArrayList<StatsLogConsumer> LOGS_CONSUMER = new CopyOnWriteArrayList<>(); - private final Context mContext; - public StatsLogCompatManager(Context context) { mContext = context; } @@ -331,6 +327,11 @@ public class StatsLogCompatManager extends StatsLogManager { if (!Utilities.ATLEAST_R) { return; } + if (DEBUG) { + String name = (event instanceof Enum) ? ((Enum) event).name() : + event.getId() + ""; + Log.d(TAG, name); + } LauncherAppState appState = LauncherAppState.getInstanceNoCreate(); if (mSlice == null && mSliceItem != null) { @@ -566,14 +567,16 @@ public class StatsLogCompatManager extends StatsLogManager { * Helps to construct and log statsd compatible impression events. */ private static class StatsCompatImpressionLogger implements StatsImpressionLogger { - private int[] mResultTypeList = new int[]{}; - private int[] mResultCountList = new int[]{}; - private final List<Boolean> mAboveKeyboardList = new ArrayList<>(); - private int[] mUidList = new int[]{}; private InstanceId mInstanceId = DEFAULT_INSTANCE_ID; private State mLauncherState = State.UNKNOWN; private int mQueryLength = -1; + // Fields used for Impression Logging V2. + private int mResultType; + private boolean mAboveKeyboard = false; + private int mUid; + private int mResultSource; + @Override public StatsImpressionLogger withInstanceId(InstanceId instanceId) { this.mInstanceId = instanceId; @@ -593,69 +596,60 @@ public class StatsLogCompatManager extends StatsLogManager { } @Override - public StatsImpressionLogger withResultType(IntArray resultType) { - mResultTypeList = resultType.toArray(); + public StatsImpressionLogger withResultType(int resultType) { + mResultType = resultType; return this; } + @Override - public StatsImpressionLogger withResultCount(IntArray resultCount) { - mResultCountList = resultCount.toArray(); + public StatsImpressionLogger withAboveKeyboard(boolean aboveKeyboard) { + mAboveKeyboard = aboveKeyboard; return this; } @Override - public StatsImpressionLogger withAboveKeyboard(List<Boolean> aboveKeyboard) { - mAboveKeyboardList.clear(); - this.mAboveKeyboardList.addAll(aboveKeyboard); + public StatsImpressionLogger withUid(int uid) { + mUid = uid; return this; } @Override - public StatsImpressionLogger withUids(IntArray uid) { - mUidList = uid.toArray(); + public StatsImpressionLogger withResultSource(int resultSource) { + mResultSource = resultSource; return this; } @Override public void log(EventEnum event) { - boolean[] mAboveKeyboard = new boolean[mAboveKeyboardList.size()]; - for (int i = 0; i < mAboveKeyboardList.size(); i++) { - mAboveKeyboard[i] = mAboveKeyboardList.get(i); - } if (IS_VERBOSE) { String name = (event instanceof Enum) ? ((Enum) event).name() : event.getId() + ""; StringBuilder logStringBuilder = new StringBuilder("\n"); logStringBuilder.append(String.format("InstanceId:%s ", mInstanceId)); logStringBuilder.append(String.format("ImpressionEvent:%s ", name)); - logStringBuilder.append(String.format("LauncherState = %s ", mLauncherState)); - logStringBuilder.append(String.format("QueryLength = %s ", mQueryLength)); - for (int i = 0; i < mResultTypeList.length; i++) { - logStringBuilder.append(String.format( - "\n ResultType = %s with ResultCount = %s with is_above_keyboard = %s" - + " with uid = %s", - mResultTypeList[i], mResultCountList[i], - mAboveKeyboard[i], mUidList[i])); - } + logStringBuilder.append(String.format("\n\tLauncherState = %s ", mLauncherState)); + logStringBuilder.append(String.format("\tQueryLength = %s ", mQueryLength)); + logStringBuilder.append(String.format( + "\n\t ResultType = %s is_above_keyboard = %s" + + " uid = %s result_source = %s", + mResultType, + mAboveKeyboard, mUid, mResultSource)); + Log.d(IMPRESSION_TAG, logStringBuilder.toString()); } - - SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_IMPRESSION_EVENT, + SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_IMPRESSION_EVENT_V2, event.getId(), // event_id mInstanceId.getId(), // instance_id mLauncherState.getLauncherState(), // state mQueryLength, // query_length - //result type list - mResultTypeList, - // result count list - mResultCountList, - // above keyboard list - mAboveKeyboard, - // uid list - mUidList + mResultType, //result type + mAboveKeyboard, // above keyboard + mUid, // uid + mResultSource // result source + ); } } diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java index 409bf9cd39..cca4d520c5 100644 --- a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java +++ b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java @@ -18,12 +18,9 @@ package com.android.quickstep.util; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.launcher3.config.FeatureFlags; - import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Locale; @@ -150,10 +147,6 @@ public class ActiveGestureLog { lastEventEntries.add(eventEntry); } - public void clear() { - Arrays.fill(logs, null); - } - public void dump(String prefix, PrintWriter writer) { writer.println(prefix + "ActiveGestureErrorDetector:"); for (int i = 0; i < logs.length; i++) { diff --git a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java index a92ab2a417..cb35ec8455 100644 --- a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java +++ b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java @@ -15,9 +15,9 @@ */ package com.android.quickstep.util; -import static com.android.launcher3.anim.Interpolators.DEACCEL; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.quickstep.AbsSwipeUpHandler.ALL_APPS_SHIFT_THRESHOLD; +import static com.android.app.animation.Interpolators.DECELERATE; +import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; @@ -34,6 +34,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherPrefs; import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; @@ -55,7 +56,7 @@ public class AnimatorControllerWithResistance { private enum RecentsResistanceParams { FROM_APP(0.75f, 0.5f, 1f, false), - FROM_APP_TO_ALL_APPS(0.75f, 0.5f, 0.8f, false), + FROM_APP_TO_ALL_APPS(1f, 0.6f, 0.8f, false), FROM_APP_TABLET(1f, 0.7f, 1f, true), FROM_APP_TO_ALL_APPS_TABLET(1f, 0.5f, 0.5f, false), FROM_OVERVIEW(1f, 0.75f, 0.5f, false); @@ -91,7 +92,7 @@ public class AnimatorControllerWithResistance { public final boolean stopScalingAtTop; } - private static final TimeInterpolator RECENTS_SCALE_RESIST_INTERPOLATOR = DEACCEL; + private static final TimeInterpolator RECENTS_SCALE_RESIST_INTERPOLATOR = DECELERATE; private static final TimeInterpolator RECENTS_TRANSLATE_RESIST_INTERPOLATOR = LINEAR; private static final Rect TEMP_RECT = new Rect(); @@ -188,7 +189,8 @@ public class AnimatorControllerWithResistance { recentsOrientedState.getOrientationHandler()); float dragLengthFactor = (float) dp.heightPx / transitionDragLength; // -1s are because 0-1 is reserved for the normal transition. - return (ALL_APPS_SHIFT_THRESHOLD - 1) / (dragLengthFactor - 1); + float threshold = LauncherPrefs.get(context).get(ALL_APPS_OVERVIEW_THRESHOLD) / 100f; + return (threshold - 1) / (dragLengthFactor - 1); } /** diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java index cbde257003..1a7099da09 100644 --- a/quickstep/src/com/android/quickstep/util/AppPairsController.java +++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java @@ -17,19 +17,30 @@ package com.android.quickstep.util; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_PAIR_LAUNCH; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import android.app.ActivityTaskManager; import android.content.Context; +import android.content.Intent; + +import androidx.annotation.Nullable; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.icons.IconCache; +import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.SplitConfigurationOptions; import com.android.quickstep.views.TaskView; +import com.android.systemui.shared.recents.model.Task; + +import java.util.Arrays; /** * Mini controller class that handles app pair interactions: saving, modifying, deleting, etc. @@ -52,10 +63,13 @@ public class AppPairsController { private final Context mContext; private final SplitSelectStateController mSplitSelectStateController; + private final StatsLogManager mStatsLogManager; public AppPairsController(Context context, - SplitSelectStateController splitSelectStateController) { + SplitSelectStateController splitSelectStateController, + StatsLogManager statsLogManager) { mContext = context; mSplitSelectStateController = splitSelectStateController; + mStatsLogManager = statsLogManager; } /** @@ -84,11 +98,51 @@ public class AppPairsController { LauncherAccessibilityDelegate delegate = Launcher.getLauncher(mContext).getAccessibilityDelegate(); if (delegate != null) { - MAIN_EXECUTOR.execute(() -> delegate.addToWorkspace(newAppPair, true)); + delegate.addToWorkspace(newAppPair, true); + mStatsLogManager.logger().withItemInfo(newAppPair) + .log(StatsLogManager.LauncherEvent.LAUNCHER_APP_PAIR_SAVE); } }); }); + } + + /** + * Launches an app pair by searching the RecentsModel for running instances of each app, and + * staging either those running instances or launching the apps as new Intents. + */ + public void launchAppPair(WorkspaceItemInfo app1, WorkspaceItemInfo app2) { + ComponentKey app1Key = new ComponentKey(app1.getTargetComponent(), app1.user); + ComponentKey app2Key = new ComponentKey(app2.getTargetComponent(), app2.user); + mSplitSelectStateController.findLastActiveTasksAndRunCallback( + Arrays.asList(app1Key, app2Key), + foundTasks -> { + @Nullable Task foundTask1 = foundTasks.get(0); + Intent task1Intent; + int task1Id; + if (foundTask1 != null) { + task1Id = foundTask1.key.id; + task1Intent = null; + } else { + task1Id = ActivityTaskManager.INVALID_TASK_ID; + task1Intent = app1.intent; + } + + mSplitSelectStateController.setInitialTaskSelect(task1Intent, + SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, + app1, + LAUNCHER_APP_PAIR_LAUNCH, + task1Id); + + @Nullable Task foundTask2 = foundTasks.get(1); + if (foundTask2 != null) { + mSplitSelectStateController.setSecondTask(foundTask2); + } else { + mSplitSelectStateController.setSecondTask( + app2.intent, app2.user); + } + mSplitSelectStateController.launchSplitTasks(); + }); } /** diff --git a/quickstep/src/com/android/quickstep/util/AssistStateManager.java b/quickstep/src/com/android/quickstep/util/AssistStateManager.java new file mode 100644 index 0000000000..e8c1a78bd7 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/AssistStateManager.java @@ -0,0 +1,51 @@ +/* + * 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.quickstep.util; + +import static com.android.launcher3.util.MainThreadInitializedObject.forOverride; + +import com.android.launcher3.R; +import com.android.launcher3.util.MainThreadInitializedObject; +import com.android.launcher3.util.ResourceBasedOverride; + +import java.io.PrintWriter; + +/** Class to manage Assistant states. */ +public class AssistStateManager implements ResourceBasedOverride { + + public static final MainThreadInitializedObject<AssistStateManager> INSTANCE = + forOverride(AssistStateManager.class, R.string.assist_state_manager_class); + + public AssistStateManager() {} + + /** Whether search is available. */ + public boolean isSearchAvailable() { + return false; + } + + /** Return {@code true} if the Settings toggle is enabled. */ + public boolean isSettingsNavHandleEnabled() { + return false; + } + + /** Return {@code true} if the Settings toggle is enabled. */ + public boolean isSettingsHomeButtonEnabled() { + return false; + } + + /** Dump states. */ + public void dump(String prefix, PrintWriter writer) {} +} diff --git a/quickstep/src/com/android/quickstep/util/AssistUtils.java b/quickstep/src/com/android/quickstep/util/AssistUtils.java new file mode 100644 index 0000000000..11b6ea7d21 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/AssistUtils.java @@ -0,0 +1,45 @@ +/* + * 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.quickstep.util; + +import android.content.Context; + +import com.android.launcher3.R; +import com.android.launcher3.util.ResourceBasedOverride; + +/** Utilities to work with Assistant functionality. */ +public class AssistUtils implements ResourceBasedOverride { + + public AssistUtils() {} + + /** Creates AssistUtils as specified by overrides */ + public static AssistUtils newInstance(Context context) { + return Overrides.getObject(AssistUtils.class, context, R.string.assist_utils_class); + } + + /** @return Array of AssistUtils.INVOCATION_TYPE_* that we want to handle instead of SysUI. */ + public int[] getSysUiAssistOverrideInvocationTypes() { + return new int[0]; + } + + /** + * @return {@code true} if the override was handled, i.e. an assist surface was shown or the + * request should be ignored. {@code false} means the caller should start assist another way. + */ + public boolean tryStartAssistOverride(int invocationType) { + return false; + } +} diff --git a/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java b/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java new file mode 100644 index 0000000000..0dee5b3103 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java @@ -0,0 +1,125 @@ +/* + * 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.quickstep.util; + +import static android.content.Intent.ACTION_TIMEZONE_CHANGED; +import static android.content.Intent.ACTION_TIME_CHANGED; + +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.provider.Settings; +import android.util.ArrayMap; +import android.widget.TextClock.ClockEventDelegate; + +import androidx.annotation.WorkerThread; + +import com.android.launcher3.util.SettingsCache; +import com.android.launcher3.util.SettingsCache.OnChangeListener; +import com.android.launcher3.util.SimpleBroadcastReceiver; + +import java.util.ArrayList; +import java.util.List; + +/** + * Extension of {@link ClockEventDelegate} to support async event registration + */ +public class AsyncClockEventDelegate extends ClockEventDelegate implements OnChangeListener { + + private final Context mContext; + private final SimpleBroadcastReceiver mReceiver = + new SimpleBroadcastReceiver(this::onClockEventReceived); + + private final ArrayMap<BroadcastReceiver, Handler> mTimeEventReceivers = new ArrayMap<>(); + private final List<ContentObserver> mFormatObservers = new ArrayList<>(); + private final Uri mFormatUri = Settings.System.getUriFor(Settings.System.TIME_12_24); + + private boolean mFormatRegistered = false; + private boolean mDestroyed = false; + + public AsyncClockEventDelegate(Context context) { + super(context); + mContext = context; + + UI_HELPER_EXECUTOR.execute(() -> + mReceiver.register(mContext, ACTION_TIME_CHANGED, ACTION_TIMEZONE_CHANGED)); + } + + @Override + public void registerTimeChangeReceiver(BroadcastReceiver receiver, Handler handler) { + synchronized (mTimeEventReceivers) { + mTimeEventReceivers.put(receiver, handler == null ? new Handler() : handler); + } + } + + @Override + public void unregisterTimeChangeReceiver(BroadcastReceiver receiver) { + synchronized (mTimeEventReceivers) { + mTimeEventReceivers.remove(receiver); + } + } + + @Override + public void registerFormatChangeObserver(ContentObserver observer, int userHandle) { + synchronized (mFormatObservers) { + if (!mFormatRegistered && !mDestroyed) { + SettingsCache.INSTANCE.get(mContext).register(mFormatUri, this); + mFormatRegistered = true; + } + mFormatObservers.add(observer); + } + } + + @Override + public void unregisterFormatChangeObserver(ContentObserver observer) { + synchronized (mFormatObservers) { + mFormatObservers.remove(observer); + } + } + + @Override + public void onSettingsChanged(boolean isEnabled) { + if (mDestroyed) { + return; + } + synchronized (mFormatObservers) { + mFormatObservers.forEach(o -> o.dispatchChange(false, mFormatUri)); + } + } + @WorkerThread + private void onClockEventReceived(Intent intent) { + if (mDestroyed) { + return; + } + synchronized (mReceiver) { + mTimeEventReceivers.forEach((r, h) -> h.post(() -> r.onReceive(mContext, intent))); + } + } + + /** + * Unregisters all system callbacks and destroys this delegate + */ + public void onDestroy() { + mDestroyed = true; + SettingsCache.INSTANCE.get(mContext).unregister(mFormatUri, this); + UI_HELPER_EXECUTOR.execute(() -> mReceiver.unregisterReceiverSafely(mContext)); + } +} diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java index 931e46876e..99f564c8f7 100644 --- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java +++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java @@ -18,6 +18,7 @@ package com.android.quickstep.util; import android.app.WallpaperManager; import android.os.IBinder; import android.util.FloatProperty; +import android.util.Log; import android.view.AttachedSurfaceControl; import android.view.SurfaceControl; @@ -50,6 +51,9 @@ public class BaseDepthController { private static final int DEPTH_INDEX_WIDGET = 1; private static final int DEPTH_INDEX_COUNT = 2; + // b/291401432 + private static final String TAG = "BaseDepthController"; + protected final Launcher mLauncher; /** Property to set the depth for state transition. */ public final MultiProperty stateDepth; @@ -88,7 +92,7 @@ public class BaseDepthController { */ protected boolean mInEarlyWakeUp; - private boolean mWaitingOnSurfaceValidity; + protected boolean mWaitingOnSurfaceValidity; public BaseDepthController(Launcher activity) { mLauncher = activity; @@ -133,9 +137,11 @@ public class BaseDepthController { return; } if (mSurface == null) { + Log.d(TAG, "mSurface is null and mCurrentBlur is: " + mCurrentBlur); return; } if (!mSurface.isValid()) { + Log.d(TAG, "mSurface is not valid"); mWaitingOnSurfaceValidity = true; onInvalidSurface(); return; @@ -186,6 +192,8 @@ public class BaseDepthController { protected void setSurface(SurfaceControl surface) { if (mSurface != surface || mWaitingOnSurfaceValidity) { mSurface = surface; + Log.d(TAG, "setSurface:\n\tmWaitingOnSurfaceValidity: " + mWaitingOnSurfaceValidity + + "\n\tmSurface: " + mSurface); applyDepthAndBlur(); } } diff --git a/quickstep/src/com/android/quickstep/util/BinderTracker.java b/quickstep/src/com/android/quickstep/util/BinderTracker.java deleted file mode 100644 index cb04e5b015..0000000000 --- a/quickstep/src/com/android/quickstep/util/BinderTracker.java +++ /dev/null @@ -1,63 +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 com.android.quickstep.util; - -import android.os.Binder; -import android.os.IBinder; -import android.os.Looper; -import android.util.Log; - -import com.android.launcher3.config.FeatureFlags; - -/** - * Utility class to test and check binder calls during development. - */ -public class BinderTracker { - - private static final String TAG = "BinderTracker"; - - public static void start() { - if (!FeatureFlags.IS_STUDIO_BUILD) { - Log.wtf(TAG, "Accessing tracker in released code.", new Exception()); - return; - } - - Binder.setProxyTransactListener(new Tracker()); - } - - public static void stop() { - if (!FeatureFlags.IS_STUDIO_BUILD) { - Log.wtf(TAG, "Accessing tracker in released code.", new Exception()); - return; - } - Binder.setProxyTransactListener(null); - } - - private static class Tracker implements Binder.ProxyTransactListener { - - @Override - public Object onTransactStarted(IBinder iBinder, int code) { - if (Looper.myLooper() == Looper.getMainLooper()) { - Log.e(TAG, "Binder call on ui thread", new Exception()); - } - return null; - } - - @Override - public void onTransactEnded(Object session) { } - } -} diff --git a/quickstep/src/com/android/quickstep/util/BorderAnimator.java b/quickstep/src/com/android/quickstep/util/BorderAnimator.java index 011d45c8e7..7563187b93 100644 --- a/quickstep/src/com/android/quickstep/util/BorderAnimator.java +++ b/quickstep/src/com/android/quickstep/util/BorderAnimator.java @@ -29,9 +29,9 @@ import android.view.animation.Interpolator; import androidx.annotation.NonNull; import androidx.annotation.Px; +import com.android.app.animation.Interpolators; import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.anim.AnimatorListeners; -import com.android.launcher3.anim.Interpolators; /** * Utility class for drawing a rounded-rect border around a view. diff --git a/quickstep/src/com/android/quickstep/util/FadeOutRemoteTransition.kt b/quickstep/src/com/android/quickstep/util/FadeOutRemoteTransition.kt new file mode 100644 index 0000000000..59ff81da4e --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/FadeOutRemoteTransition.kt @@ -0,0 +1,86 @@ +/* + * 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.quickstep.util + +import android.animation.ValueAnimator +import android.os.IBinder +import android.os.RemoteException +import android.view.SurfaceControl +import android.view.SurfaceControl.Transaction +import android.window.IRemoteTransition +import android.window.IRemoteTransitionFinishedCallback +import android.window.TransitionInfo +import com.android.launcher3.anim.AnimatorListeners.forEndCallback +import com.android.launcher3.util.Executors +import com.android.wm.shell.util.TransitionUtil + +/** Remote animation which fades out the closing targets */ +class FadeOutRemoteTransition : IRemoteTransition.Stub() { + + override fun mergeAnimation( + iBinder: IBinder, + transitionInfo: TransitionInfo, + transaction: Transaction, + mergeTarget: IBinder, + finishCB: IRemoteTransitionFinishedCallback + ) { + + try { + finishCB.onTransitionFinished(null, Transaction()) + } catch (e: RemoteException) { + // Ignore + } + } + + override fun startAnimation( + transition: IBinder, + info: TransitionInfo, + startT: Transaction, + finishCB: IRemoteTransitionFinishedCallback + ) { + val anim = ValueAnimator.ofFloat(1f, 0f) + + val closingControls: MutableList<SurfaceControl> = mutableListOf() + for (chg in info.changes) { + startT.show(chg.leash) + if (TransitionUtil.isClosingType(chg.mode)) { + closingControls.add(chg.leash) + } + } + startT.apply() + + anim.addUpdateListener { + val t = Transaction() + closingControls.forEach { t.setAlpha(it, anim.animatedValue as Float) } + t.apply() + } + anim.addListener( + forEndCallback( + Runnable { + val t = Transaction() + closingControls.forEach { t.hide(it) } + try { + finishCB.onTransitionFinished(null, t) + } catch (e: RemoteException) { + // Ignore + } + } + ) + ) + + Executors.MAIN_EXECUTOR.execute { anim.start() } + } +} diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.java b/quickstep/src/com/android/quickstep/util/GroupTask.java index 2be4f0a519..9c49647c41 100644 --- a/quickstep/src/com/android/quickstep/util/GroupTask.java +++ b/quickstep/src/com/android/quickstep/util/GroupTask.java @@ -37,6 +37,10 @@ public class GroupTask { @TaskView.Type public final int taskViewType; + public GroupTask(@NonNull Task task) { + this(task, null, null); + } + public GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds) { this(t1, t2, splitBounds, t2 != null ? TaskView.Type.GROUPED : TaskView.Type.SINGLE); } diff --git a/quickstep/src/com/android/quickstep/util/LogUtils.kt b/quickstep/src/com/android/quickstep/util/LogUtils.kt index 23a41f6068..e34c4ec8a5 100644 --- a/quickstep/src/com/android/quickstep/util/LogUtils.kt +++ b/quickstep/src/com/android/quickstep/util/LogUtils.kt @@ -20,6 +20,11 @@ import com.android.internal.logging.InstanceIdSequence import com.android.launcher3.logging.InstanceId object LogUtils { + @JvmStatic + fun splitFailureMessage(caller: String, reason: String): String { + return "($caller) Splitscreen aborted: $reason" + } + /** * @return a [Pair] of two InstanceIds but with different types, one that can be used by * framework (if needing to pass through an intent or such) and one used in Launcher diff --git a/quickstep/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/src/com/android/quickstep/util/NavBarPosition.java index 59c82633d2..94185120b4 100644 --- a/quickstep/src/com/android/quickstep/util/NavBarPosition.java +++ b/quickstep/src/com/android/quickstep/util/NavBarPosition.java @@ -27,25 +27,22 @@ import com.android.launcher3.util.NavigationMode; */ public class NavBarPosition { + private final boolean mIsTablet; private final NavigationMode mMode; private final int mDisplayRotation; public NavBarPosition(NavigationMode mode, Info info) { + mIsTablet = info.isTablet(info.realBounds); mMode = mode; mDisplayRotation = info.rotation; } - public NavBarPosition(NavigationMode mode, int displayRotation) { - mMode = mode; - mDisplayRotation = displayRotation; - } - public boolean isRightEdge() { - return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_90; + return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_90 && !mIsTablet; } public boolean isLeftEdge() { - return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270; + return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270 && !mIsTablet; } public float getRotation() { diff --git a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java index 3cec1a4f1a..6d9ecd9534 100644 --- a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java +++ b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java @@ -22,12 +22,16 @@ import android.animation.Animator; import android.animation.AnimatorSet; import android.util.Log; +import androidx.annotation.Nullable; + import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.states.StateAnimationConfig; +import java.util.function.Consumer; + /** * Runs an animation from overview to home. Currently, this animation is just a wrapper around the * normal state transition and may play a {@link WorkspaceRevealAnim} if we're starting from an @@ -39,14 +43,18 @@ public class OverviewToHomeAnim { private final Launcher mLauncher; private final Runnable mOnReachedHome; + @Nullable + private final Consumer<AnimatorSet> mSplitCancelConsumer; // Only run mOnReachedHome when both of these are true. private boolean mIsHomeStaggeredAnimFinished; private boolean mIsOverviewHidden; - public OverviewToHomeAnim(Launcher launcher, Runnable onReachedHome) { + public OverviewToHomeAnim(Launcher launcher, Runnable onReachedHome, + @Nullable Consumer<AnimatorSet> splitCancelConsumer) { mLauncher = launcher; mOnReachedHome = onReachedHome; + mSplitCancelConsumer = splitCancelConsumer; } /** @@ -92,6 +100,11 @@ public class OverviewToHomeAnim { maybeOverviewToHomeAnimComplete(); } }); + + if (mSplitCancelConsumer != null) { + // Clear split state when swiping to home + mSplitCancelConsumer.accept(anim); + } anim.play(stateAnim); stateManager.setCurrentAnimation(anim, NORMAL); anim.start(); diff --git a/quickstep/src/com/android/quickstep/util/OverviewToSplitTimings.java b/quickstep/src/com/android/quickstep/util/OverviewToSplitTimings.java index e189a66ee4..3027f79f03 100644 --- a/quickstep/src/com/android/quickstep/util/OverviewToSplitTimings.java +++ b/quickstep/src/com/android/quickstep/util/OverviewToSplitTimings.java @@ -16,8 +16,8 @@ package com.android.quickstep.util; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED; -import static com.android.launcher3.anim.Interpolators.INSTANT; +import static com.android.app.animation.Interpolators.EMPHASIZED; +import static com.android.app.animation.Interpolators.INSTANT; import android.view.animation.Interpolator; diff --git a/quickstep/src/com/android/quickstep/util/PhoneOverviewToSplitTimings.java b/quickstep/src/com/android/quickstep/util/PhoneOverviewToSplitTimings.java index f1dde53d12..a38f437664 100644 --- a/quickstep/src/com/android/quickstep/util/PhoneOverviewToSplitTimings.java +++ b/quickstep/src/com/android/quickstep/util/PhoneOverviewToSplitTimings.java @@ -16,7 +16,7 @@ package com.android.quickstep.util; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED; +import static com.android.app.animation.Interpolators.EMPHASIZED; import android.view.animation.Interpolator; diff --git a/quickstep/src/com/android/quickstep/util/ProtoTracer.java b/quickstep/src/com/android/quickstep/util/ProtoTracer.java deleted file mode 100644 index ef9586d693..0000000000 --- a/quickstep/src/com/android/quickstep/util/ProtoTracer.java +++ /dev/null @@ -1,135 +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 com.android.quickstep.util; - -import static com.android.launcher3.tracing.LauncherTraceFileProto.MagicNumber.MAGIC_NUMBER_H_VALUE; -import static com.android.launcher3.tracing.LauncherTraceFileProto.MagicNumber.MAGIC_NUMBER_L_VALUE; - -import android.content.Context; -import android.os.SystemClock; - -import android.os.Trace; -import com.android.launcher3.tracing.LauncherTraceProto; -import com.android.launcher3.tracing.LauncherTraceEntryProto; -import com.android.launcher3.tracing.LauncherTraceFileProto; -import com.android.launcher3.util.MainThreadInitializedObject; -import com.android.systemui.shared.tracing.FrameProtoTracer; -import com.android.systemui.shared.tracing.FrameProtoTracer.ProtoTraceParams; -import com.android.systemui.shared.tracing.ProtoTraceable; -import com.google.protobuf.MessageLite; - -import java.io.File; -import java.util.ArrayList; -import java.util.Queue; - - -/** - * Controller for coordinating winscope proto tracing. - */ -public class ProtoTracer implements ProtoTraceParams<MessageLite.Builder, - LauncherTraceFileProto.Builder, LauncherTraceEntryProto.Builder, - LauncherTraceProto.Builder> { - - public static final MainThreadInitializedObject<ProtoTracer> INSTANCE = - new MainThreadInitializedObject<>(ProtoTracer::new); - - private static final String TAG = "ProtoTracer"; - private static final long MAGIC_NUMBER_VALUE = - ((long) MAGIC_NUMBER_H_VALUE << 32) | MAGIC_NUMBER_L_VALUE; - - private final Context mContext; - private final FrameProtoTracer<MessageLite.Builder, LauncherTraceFileProto.Builder, - LauncherTraceEntryProto.Builder, LauncherTraceProto.Builder> mProtoTracer; - - public ProtoTracer(Context context) { - mContext = context; - mProtoTracer = new FrameProtoTracer<>(this); - } - - @Override - public File getTraceFile() { - return new File(mContext.getFilesDir(), "launcher_trace.pb"); - } - - @Override - public LauncherTraceFileProto.Builder getEncapsulatingTraceProto() { - return LauncherTraceFileProto.newBuilder(); - } - - @Override - public LauncherTraceEntryProto.Builder updateBufferProto( - LauncherTraceEntryProto.Builder reuseObj, - ArrayList<ProtoTraceable<LauncherTraceProto.Builder>> traceables) { - Trace.beginSection("ProtoTracer.updateBufferProto"); - LauncherTraceEntryProto.Builder proto = LauncherTraceEntryProto.newBuilder(); - proto.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); - LauncherTraceProto.Builder launcherProto = LauncherTraceProto.newBuilder(); - for (ProtoTraceable t : traceables) { - t.writeToProto(launcherProto); - } - proto.setLauncher(launcherProto); - Trace.endSection(); - return proto; - } - - @Override - public byte[] serializeEncapsulatingProto(LauncherTraceFileProto.Builder encapsulatingProto, - Queue<LauncherTraceEntryProto.Builder> buffer) { - Trace.beginSection("ProtoTracer.serializeEncapsulatingProto"); - encapsulatingProto.setMagicNumber(MAGIC_NUMBER_VALUE); - for (LauncherTraceEntryProto.Builder entry : buffer) { - encapsulatingProto.addEntry(entry); - } - byte[] bytes = encapsulatingProto.build().toByteArray(); - Trace.endSection(); - return bytes; - } - - @Override - public byte[] getProtoBytes(MessageLite.Builder proto) { - return proto.build().toByteArray(); - } - - @Override - public int getProtoSize(MessageLite.Builder proto) { - return proto.build().getSerializedSize(); - } - - public void start() { - mProtoTracer.start(); - } - - public void stop() { - mProtoTracer.stop(); - } - - public void add(ProtoTraceable<LauncherTraceProto.Builder> traceable) { - mProtoTracer.add(traceable); - } - - public void remove(ProtoTraceable<LauncherTraceProto.Builder> traceable) { - mProtoTracer.remove(traceable); - } - - public void scheduleFrameUpdate() { - mProtoTracer.scheduleFrameUpdate(); - } - - public void update() { - mProtoTracer.update(); - } -} diff --git a/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java b/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java deleted file mode 100644 index 8f79ccf450..0000000000 --- a/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2021 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.quickstep.util; - -import androidx.annotation.NonNull; - -import com.android.systemui.unfold.updates.screen.ScreenStatusProvider; - -import java.util.ArrayList; -import java.util.List; - -/** - * Screen status provider implementation that exposes methods to provide screen - * status updates to listeners. It is used to receive screen turned on event from - * SystemUI to Launcher. - */ -public class ProxyScreenStatusProvider implements ScreenStatusProvider { - - public static final ProxyScreenStatusProvider INSTANCE = new ProxyScreenStatusProvider(); - private final List<ScreenListener> mListeners = new ArrayList<>(); - - /** - * Called when the screen is on and ready (windows are drawn and screen blocker is removed) - */ - public void onScreenTurnedOn() { - mListeners.forEach(ScreenListener::onScreenTurnedOn); - } - - /** Called when the screen is starting to turn on. */ - public void onScreenTurningOn() { - mListeners.forEach(ScreenListener::onScreenTurningOn); - } - - /** Called when the screen is starting to turn off. */ - public void onScreenTurningOff() { - mListeners.forEach(ScreenListener::onScreenTurningOff); - } - - @Override - public void addCallback(@NonNull ScreenListener listener) { - mListeners.add(listener); - } - - @Override - public void removeCallback(@NonNull ScreenListener listener) { - mListeners.remove(listener); - } -} diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java deleted file mode 100644 index 10f2eaa2f0..0000000000 --- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2018 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.quickstep.util; - -import android.animation.AnimatorSet; -import android.view.RemoteAnimationTarget; - -public abstract class RemoteAnimationProvider { - - public abstract AnimatorSet createWindowAnimation(RemoteAnimationTarget[] appTargets, - RemoteAnimationTarget[] wallpaperTargets); - - /** - * @return the target with the lowest opaque layer for a certain app animation, or null. - */ - public static RemoteAnimationTarget findLowestOpaqueLayerTarget( - RemoteAnimationTarget[] appTargets, int mode) { - int lowestLayer = Integer.MAX_VALUE; - int lowestLayerIndex = -1; - for (int i = appTargets.length - 1; i >= 0; i--) { - RemoteAnimationTarget target = appTargets[i]; - if (target.mode == mode && !target.isTranslucent) { - int layer = target.prefixOrderIndex; - if (layer < lowestLayer) { - lowestLayer = layer; - lowestLayerIndex = i; - } - } - } - return lowestLayerIndex != -1 - ? appTargets[lowestLayerIndex] - : null; - } -} diff --git a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java deleted file mode 100644 index 382cf79e1e..0000000000 --- a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2018 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.quickstep.util; - -import static android.view.RemoteAnimationTarget.MODE_CLOSING; - -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl.Transaction; - -import com.android.quickstep.RemoteAnimationTargets; - -/** - * Animation listener which fades out the closing targets - */ -public class RemoteFadeOutAnimationListener implements AnimatorUpdateListener { - - private final RemoteAnimationTargets mTarget; - private boolean mFirstFrame = true; - - public RemoteFadeOutAnimationListener(RemoteAnimationTarget[] appTargets, - RemoteAnimationTarget[] wallpaperTargets) { - mTarget = new RemoteAnimationTargets(appTargets, wallpaperTargets, - new RemoteAnimationTarget[0], MODE_CLOSING); - } - - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - Transaction t = new Transaction(); - if (mFirstFrame) { - for (RemoteAnimationTarget target : mTarget.unfilteredApps) { - t.show(target.leash); - } - mFirstFrame = false; - } - - float alpha = 1 - valueAnimator.getAnimatedFraction(); - for (RemoteAnimationTarget app : mTarget.apps) { - t.setAlpha(app.leash, alpha); - } - t.apply(); - } -} diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt index b76fe5cb03..c3774eb65b 100644 --- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt +++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt @@ -17,14 +17,26 @@ package com.android.quickstep.util +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.AnimatorSet import android.animation.ObjectAnimator import android.graphics.Bitmap +import android.graphics.Rect +import android.graphics.RectF import android.graphics.drawable.Drawable import android.view.View +import com.android.app.animation.Interpolators import com.android.launcher3.DeviceProfile +import com.android.launcher3.Utilities import com.android.launcher3.anim.PendingAnimation +import com.android.launcher3.statemanager.StatefulActivity import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource +import com.android.launcher3.views.BaseDragLayer +import com.android.quickstep.views.FloatingTaskView import com.android.quickstep.views.IconView +import com.android.quickstep.views.RecentsView +import com.android.quickstep.views.SplitInstructionsView import com.android.quickstep.views.TaskThumbnailView import com.android.quickstep.views.TaskView import com.android.quickstep.views.TaskView.TaskIdAttributeContainer @@ -176,4 +188,91 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC TaskThumbnailView.SPLIT_SELECT_TRANSLATE_X, 0f)) } } + + /** Does not play any animation if user is not currently in split selection state. */ + fun playPlaceholderDismissAnim(launcher: StatefulActivity<*>) { + if (!splitSelectStateController.isSplitSelectActive) { + return + } + + val anim = createPlaceholderDismissAnim(launcher) + anim.start() + } + + /** Returns [AnimatorSet] which slides initial split placeholder view offscreen. */ + fun createPlaceholderDismissAnim(launcher: StatefulActivity<*>) : AnimatorSet { + val animatorSet = AnimatorSet() + val recentsView : RecentsView<*, *> = launcher.getOverviewPanel() + val floatingTask: FloatingTaskView = splitSelectStateController.firstFloatingTaskView + ?: return animatorSet + + // We are in split selection state currently, transitioning to another state + val dragLayer: BaseDragLayer<*> = launcher.dragLayer + val onScreenRectF = RectF() + Utilities.getBoundsForViewInDragLayer(dragLayer, floatingTask, + Rect(0, 0, floatingTask.width, floatingTask.height), + false, null, onScreenRectF) + // Get the part of the floatingTask that intersects with the DragLayer (i.e. the + // on-screen portion) + onScreenRectF.intersect( + dragLayer.left.toFloat(), + dragLayer.top.toFloat(), + dragLayer.right.toFloat(), + dragLayer.bottom + .toFloat() + ) + animatorSet.play(ObjectAnimator.ofFloat(floatingTask, + FloatingTaskView.PRIMARY_TRANSLATE_OFFSCREEN, + recentsView.pagedOrientationHandler + .getFloatingTaskOffscreenTranslationTarget( + floatingTask, + onScreenRectF, + floatingTask.stagePosition, + launcher.deviceProfile + ))) + animatorSet.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + splitSelectStateController.resetState() + safeRemoveViewFromDragLayer(launcher, + splitSelectStateController.splitInstructionsView) + } + }) + return animatorSet + } + + /** + * Returns a [PendingAnimation] to animate in the chip to instruct a user to select a second + * app for splitscreen + */ + fun getShowSplitInstructionsAnim(launcher: StatefulActivity<*>) : PendingAnimation { + safeRemoveViewFromDragLayer(launcher, splitSelectStateController.splitInstructionsView) + val splitInstructionsView = SplitInstructionsView.getSplitInstructionsView(launcher) + splitSelectStateController.splitInstructionsView = splitInstructionsView + val timings = AnimUtils.getDeviceOverviewToSplitTimings(launcher.deviceProfile.isTablet) + val anim = PendingAnimation(100 /*duration */) + anim.setViewAlpha(splitInstructionsView, 1f, + Interpolators.clampToProgress(Interpolators.LINEAR, + timings.instructionsContainerFadeInStartOffset, + timings.instructionsContainerFadeInEndOffset)) + anim.setViewAlpha(splitInstructionsView!!.textView, 1f, + Interpolators.clampToProgress(Interpolators.LINEAR, + timings.instructionsTextFadeInStartOffset, + timings.instructionsTextFadeInEndOffset)) + anim.addFloat(splitInstructionsView, SplitInstructionsView.UNFOLD, 0.1f, 1f, + Interpolators.clampToProgress(Interpolators.EMPHASIZED_DECELERATE, + timings.instructionsUnfoldStartOffset, + timings.instructionsUnfoldEndOffset)) + return anim + } + + /** Removes the split instructions view from [launcher] drag layer. */ + fun removeSplitInstructionsView(launcher: StatefulActivity<*>) { + safeRemoveViewFromDragLayer(launcher, splitSelectStateController.splitInstructionsView) + } + + private fun safeRemoveViewFromDragLayer(launcher: StatefulActivity<*>, view: View?) { + if (view != null) { + launcher.dragLayer.removeView(view) + } + } } diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java b/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java index 7dc1b32858..93f2255a4d 100644 --- a/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java +++ b/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java @@ -16,7 +16,7 @@ package com.android.quickstep.util; -import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.LINEAR; import android.view.animation.Interpolator; diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt index 3ff461adf3..69c4197d36 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt +++ b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt @@ -45,7 +45,11 @@ import java.io.PrintWriter * * After setting the correct fields for initial/second.* variables, this converts them into the * correct [PendingIntent] and [ShortcutInfo] objects where applicable and sends the necessary - * data back via [getSplitLaunchData]. + * data back via [getSplitLaunchData]. Note: there should be only one "initial" field and one + * "second" field set, with the rest remaining null. (Exception: [Intent] and [UserHandle] are + * always passed in together as a set, and are converted to a single [PendingIntent] or + * [ShortcutInfo]+[PendingIntent] before launch.) + * * [SplitLaunchType] indicates the type of tasks/apps/intents being launched given the provided * state */ @@ -80,21 +84,21 @@ class SplitSelectDataHolder( const val SPLIT_SINGLE_SHORTCUT_FULLSCREEN = 8 } - @StagePosition private var initialStagePosition: Int = STAGE_POSITION_UNDEFINED + private var itemInfo: ItemInfo? = null + private var splitEvent: EventEnum? = null + private var initialTaskId: Int = INVALID_TASK_ID private var secondTaskId: Int = INVALID_TASK_ID - private var initialUser: UserHandle? = null - private var secondUser: UserHandle? = null private var initialIntent: Intent? = null private var secondIntent: Intent? = null + private var initialUser: UserHandle? = null + private var secondUser: UserHandle? = null + private var initialPendingIntent: PendingIntent? = null private var secondPendingIntent: PendingIntent? = null - private var itemInfo: ItemInfo? = null - private var splitEvent: EventEnum? = null private var initialShortcut: ShortcutInfo? = null private var secondShortcut: ShortcutInfo? = null - private var initialPendingIntent: PendingIntent? = null /** * @param alreadyRunningTask if set to [android.app.ActivityTaskManager.INVALID_TASK_ID] @@ -222,7 +226,7 @@ class SplitSelectDataHolder( * split task in fullscreen */ fun getFullscreenLaunchData() : SplitLaunchData { - // Convert all intents to shortcut infos to see if determine if we launch shortcut or intent + // Convert all intents to shortcut infos to determine if we launch shortcut or intent convertIntentsToFinalTypes() val splitLaunchType = getFullscreenLaunchType() @@ -255,7 +259,7 @@ class SplitSelectDataHolder( * convert [secondIntent] */ private fun convertIntentsToFinalTypes() { - initialShortcut = getShortcutInfo(initialIntent, checkNotNull(initialUser)) + initialShortcut = getShortcutInfo(initialIntent, initialUser) initialPendingIntent = getPendingIntent(initialIntent, initialUser) initialIntent = null @@ -269,7 +273,7 @@ class SplitSelectDataHolder( return } - secondShortcut = getShortcutInfo(secondIntent, checkNotNull(secondUser)) + secondShortcut = getShortcutInfo(secondIntent, secondUser) secondPendingIntent = getPendingIntent(secondIntent, secondUser) secondIntent = null } @@ -375,6 +379,18 @@ class SplitSelectDataHolder( return secondTaskId } + fun getSplitEvent(): EventEnum? { + return splitEvent + } + + fun getInitialStagePosition(): Int { + return initialStagePosition + } + + fun getItemInfo(): ItemInfo? { + return itemInfo + } + private fun isSecondTaskIntentSet(): Boolean { return secondTaskId != INVALID_TASK_ID || secondIntent != null || secondPendingIntent != null diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java index f25619d963..d4ddf76c41 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -16,14 +16,16 @@ package com.android.quickstep.util; -import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import static android.app.PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT; -import static android.app.PendingIntent.FLAG_MUTABLE; - import static com.android.launcher3.Utilities.postAsyncCallback; +import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DESKTOP_MODE_SPLIT_LEFT_TOP; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM; +import static com.android.launcher3.testing.shared.TestProtocol.LAUNCH_SPLIT_PAIR; +import static com.android.launcher3.testing.shared.TestProtocol.testLogD; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO; -import static com.android.launcher3.util.SplitConfigurationOptions.getOppositeStagePosition; +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT; import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_PENDINGINTENT_PENDINGINTENT; import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_PENDINGINTENT_TASK; import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_SHORTCUT_TASK; @@ -34,6 +36,8 @@ import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_PENDIN import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_SHORTCUT; import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_TASK; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -43,10 +47,14 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; import android.util.Log; import android.util.Pair; @@ -61,28 +69,42 @@ import android.window.TransitionInfo; import androidx.annotation.Nullable; import com.android.internal.logging.InstanceId; +import com.android.launcher3.Launcher; +import com.android.launcher3.R; +import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.icons.IconProvider; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.ComponentKey; -import com.android.launcher3.util.SplitConfigurationOptions; import com.android.launcher3.util.SplitConfigurationOptions.StagePosition; +import com.android.quickstep.OverviewComponentObserver; +import com.android.quickstep.RecentsAnimationCallbacks; +import com.android.quickstep.RecentsAnimationController; +import com.android.quickstep.RecentsAnimationDeviceState; +import com.android.quickstep.RecentsAnimationTargets; import com.android.quickstep.RecentsModel; +import com.android.quickstep.SplitSelectionListener; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskAnimationManager; import com.android.quickstep.TaskViewUtils; import com.android.quickstep.views.FloatingTaskView; import com.android.quickstep.views.GroupedTaskView; -import com.android.quickstep.views.TaskView; +import com.android.quickstep.views.SplitInstructionsView; +import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; +import com.android.wm.shell.splitscreen.ISplitSelectListener; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.function.Consumer; /** @@ -92,36 +114,18 @@ import java.util.function.Consumer; public class SplitSelectStateController { private static final String TAG = "SplitSelectStateCtor"; - private final Context mContext; + private Context mContext; private final Handler mHandler; private final RecentsModel mRecentTasksModel; private final SplitAnimationController mSplitAnimationController; private final AppPairsController mAppPairsController; private final SplitSelectDataHolder mSplitSelectDataHolder; - private StatsLogManager mStatsLogManager; + private final StatsLogManager mStatsLogManager; private final SystemUiProxy mSystemUiProxy; private final StateManager mStateManager; + private SplitFromDesktopController mSplitFromDesktopController; @Nullable private DepthController mDepthController; - private @StagePosition int mInitialStagePosition; - private ItemInfo mItemInfo; - /** {@link #mInitialTaskIntent} and {@link #mInitialUser} (the user of the Intent) are set - * together when split is initiated from an Intent. */ - private Intent mInitialTaskIntent; - private UserHandle mInitialUser; - private int mInitialTaskId = INVALID_TASK_ID; - /** {@link #mSecondTaskIntent} and {@link #mSecondUser} (the user of the Intent) are set - * together when split is confirmed with an Intent. Either this or {@link #mSecondPendingIntent} - * will be set, but not both - */ - private Intent mSecondTaskIntent; - /** - * Set when split is confirmed via a widget. Either this or {@link #mSecondTaskIntent} will be - * set, but not both - */ - private PendingIntent mSecondPendingIntent; - private UserHandle mSecondUser; - private int mSecondTaskId = INVALID_TASK_ID; private boolean mRecentsAnimationRunning; /** If {@code true}, animates the existing task view split placeholder view */ private boolean mAnimateCurrentTaskDismissal; @@ -133,10 +137,11 @@ public class SplitSelectStateController { /** If not null, this is the TaskView we want to launch from */ @Nullable private GroupedTaskView mLaunchingTaskView; - /** Represents where split is intended to be invoked from. */ - private StatsLogManager.EventEnum mSplitEvent; private FloatingTaskView mFirstFloatingTaskView; + private SplitInstructionsView mSplitInstructionsView; + + private final List<SplitSelectionListener> mSplitSelectionListeners = new ArrayList<>(); public SplitSelectStateController(Context context, Handler handler, StateManager stateManager, DepthController depthController, StatsLogManager statsLogManager, @@ -149,10 +154,14 @@ public class SplitSelectStateController { mDepthController = depthController; mRecentTasksModel = recentsModel; mSplitAnimationController = new SplitAnimationController(this); - mAppPairsController = new AppPairsController(context, this); + mAppPairsController = new AppPairsController(context, this, statsLogManager); mSplitSelectDataHolder = new SplitSelectDataHolder(mContext); } + public void onDestroy() { + mContext = null; + } + /** * @param alreadyRunningTask if set to {@link android.app.ActivityTaskManager#INVALID_TASK_ID} * then @param intent will be used to launch the initial task @@ -161,19 +170,8 @@ public class SplitSelectStateController { public void setInitialTaskSelect(@Nullable Intent intent, @StagePosition int stagePosition, @NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent, int alreadyRunningTask) { - if (alreadyRunningTask != INVALID_TASK_ID) { - mInitialTaskId = alreadyRunningTask; - } else { - mInitialTaskIntent = intent; - mInitialUser = itemInfo.user; - } - - setInitialData(stagePosition, splitEvent, itemInfo); - - if (FeatureFlags.ENABLE_SPLIT_LAUNCH_DATA_REFACTOR.get()) { - mSplitSelectDataHolder.setInitialTaskSelect(intent, stagePosition, itemInfo, splitEvent, - alreadyRunningTask); - } + mSplitSelectDataHolder.setInitialTaskSelect(intent, stagePosition, itemInfo, splitEvent, + alreadyRunningTask); } /** @@ -183,53 +181,50 @@ public class SplitSelectStateController { public void setInitialTaskSelect(ActivityManager.RunningTaskInfo info, @StagePosition int stagePosition, @NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent) { - mInitialTaskId = info.taskId; - setInitialData(stagePosition, splitEvent, itemInfo); - - if (FeatureFlags.ENABLE_SPLIT_LAUNCH_DATA_REFACTOR.get()) { - mSplitSelectDataHolder.setInitialTaskSelect(info, stagePosition, itemInfo, splitEvent); - } - } - - private void setInitialData(@StagePosition int stagePosition, - StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) { - mItemInfo = itemInfo; - mInitialStagePosition = stagePosition; - mSplitEvent = splitEvent; + mSplitSelectDataHolder.setInitialTaskSelect(info, stagePosition, itemInfo, splitEvent); } /** - * Pulls the list of active Tasks from RecentsModel, and finds the most recently active Task - * matching a given ComponentName. Then uses that Task (which could be null) with the given - * callback. + * Maps a List<ComponentKey> to List<@Nullable Task>, searching through active Tasks in + * RecentsModel. If found, the Task will be the most recently-interacted-with instance of that + * Task. Then runs the given callback on that List. * <p> * Used in various task-switching or splitscreen operations when we need to check if there is a * currently running Task of a certain type and use the most recent one. */ - public void findLastActiveTaskAndRunCallback( - @Nullable ComponentKey componentKey, Consumer<Task> callback) { + public void findLastActiveTasksAndRunCallback( + @Nullable List<ComponentKey> componentKeys, Consumer<List<Task>> callback) { mRecentTasksModel.getTasks(taskGroups -> { - if (componentKey == null) { - callback.accept(null); + if (componentKeys == null || componentKeys.isEmpty()) { + callback.accept(Collections.emptyList()); return; } - Task lastActiveTask = null; - // Loop through tasks in reverse, since they are ordered with most-recent tasks last. - for (int i = taskGroups.size() - 1; i >= 0; i--) { - GroupTask groupTask = taskGroups.get(i); - Task task1 = groupTask.task1; - if (isInstanceOfComponent(task1, componentKey)) { - lastActiveTask = task1; - break; - } - Task task2 = groupTask.task2; - if (isInstanceOfComponent(task2, componentKey)) { - lastActiveTask = task2; - break; + + List<Task> lastActiveTasks = new ArrayList<>(); + // For each key we are looking for, add to lastActiveTasks with the corresponding Task + // (or null if not found). + for (ComponentKey key : componentKeys) { + Task lastActiveTask = null; + // Loop through tasks in reverse, since they are ordered with most-recent tasks last + for (int i = taskGroups.size() - 1; i >= 0; i--) { + GroupTask groupTask = taskGroups.get(i); + Task task1 = groupTask.task1; + // Don't add duplicate Tasks + if (isInstanceOfComponent(task1, key) && !lastActiveTasks.contains(task1)) { + lastActiveTask = task1; + break; + } + Task task2 = groupTask.task2; + if (isInstanceOfComponent(task2, key) && !lastActiveTasks.contains(task2)) { + lastActiveTask = task2; + break; + } } + + lastActiveTasks.add(lastActiveTask); } - callback.accept(lastActiveTask); + callback.accept(lastActiveTasks); }); } @@ -239,7 +234,7 @@ public class SplitSelectStateController { */ public boolean isInstanceOfComponent(@Nullable Task task, @NonNull ComponentKey componentKey) { // Exclude the task that is already staged - if (task == null || task.key.id == mInitialTaskId) { + if (task == null || task.key.id == mSplitSelectDataHolder.getInitialTaskId()) { return false; } @@ -248,20 +243,48 @@ public class SplitSelectStateController { } /** - * To be called when the actual tasks ({@link #mInitialTaskId}, {@link #mSecondTaskId}) are - * to be launched. Call after launcher side animations are complete. + * Listener will only get callbacks going forward from the point of registration. No + * methods will be fired upon registering. + */ + public void registerSplitListener(@NonNull SplitSelectionListener listener) { + if (mSplitSelectionListeners.contains(listener)) { + return; + } + mSplitSelectionListeners.add(listener); + } + + public void unregisterSplitListener(@NonNull SplitSelectionListener listener) { + mSplitSelectionListeners.remove(listener); + } + + private void dispatchOnSplitSelectionExit() { + for (SplitSelectionListener listener : mSplitSelectionListeners) { + listener.onSplitSelectionExit(false); + } + } + + /** + * To be called when the both split tasks are ready to be launched. Call after launcher side + * animations are complete. */ - public void launchSplitTasks(Consumer<Boolean> callback) { + public void launchSplitTasks(@Nullable Consumer<Boolean> callback) { Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds = LogUtils.getShellShareableInstanceId(); - launchTasks(mInitialTaskId, mInitialTaskIntent, mSecondTaskId, mSecondTaskIntent, - mInitialStagePosition, callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO, + launchTasks(callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO, instanceIds.first); mStatsLogManager.logger() - .withItemInfo(mItemInfo) + .withItemInfo(mSplitSelectDataHolder.getItemInfo()) .withInstanceId(instanceIds.second) - .log(mSplitEvent); + .log(mSplitSelectDataHolder.getSplitEvent()); + } + + /** + * A version of {@link #launchTasks(Consumer, boolean, float, InstanceId)} with no success + * callback. + */ + public void launchSplitTasks() { + launchSplitTasks(null); } /** @@ -269,11 +292,7 @@ public class SplitSelectStateController { * @param task The second task that will be launched. */ public void setSecondTask(Task task) { - mSecondTaskId = task.key.id; - - if (FeatureFlags.ENABLE_SPLIT_LAUNCH_DATA_REFACTOR.get()) { - mSplitSelectDataHolder.setSecondTask(task.key.id); - } + mSplitSelectDataHolder.setSecondTask(task.key.id); } /** @@ -282,108 +301,29 @@ public class SplitSelectStateController { * @param user The user of that intent. */ public void setSecondTask(Intent intent, UserHandle user) { - mSecondTaskIntent = intent; - mSecondUser = user; - - if (FeatureFlags.ENABLE_SPLIT_LAUNCH_DATA_REFACTOR.get()) { - mSplitSelectDataHolder.setSecondTask(intent, user); - } + mSplitSelectDataHolder.setSecondTask(intent, user); } /** * To be called as soon as user selects the second app (even if animations aren't complete) - * Sets {@link #mSecondUser} from that of the pendingIntent * @param pendingIntent The second PendingIntent that will be launched. */ public void setSecondTask(PendingIntent pendingIntent) { - mSecondPendingIntent = pendingIntent; - mSecondUser = pendingIntent.getCreatorUserHandle(); - - if (FeatureFlags.ENABLE_SPLIT_LAUNCH_DATA_REFACTOR.get()) { - mSplitSelectDataHolder.setSecondTask(pendingIntent); - } + mSplitSelectDataHolder.setSecondTask(pendingIntent); } /** * To be called when we want to launch split pairs from Overview. Split can be initiated from * either Overview or home, or all apps. Either both taskIds are set, or a pending intent + a * fill in intent with a taskId2 are set. - * @param intent1 is null when split is initiated from Overview - * @param stagePosition representing location of task1 * @param shellInstanceId loggingId to be used by shell, will be non-null for actions that * create a split instance, null for cases that bring existing instaces to the * foreground (quickswitch, launching previous pairs from overview) */ - public void launchTasks(int taskId1, @Nullable Intent intent1, int taskId2, - @Nullable Intent intent2, @StagePosition int stagePosition, - Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio, - @Nullable InstanceId shellInstanceId) { + public void launchTasks(@Nullable Consumer<Boolean> callback, boolean freezeTaskList, + float splitRatio, @Nullable InstanceId shellInstanceId) { TestLogging.recordEvent( TestProtocol.SEQUENCE_MAIN, "launchSplitTasks"); - if (FeatureFlags.ENABLE_SPLIT_LAUNCH_DATA_REFACTOR.get()) { - launchTasksRefactored(callback, freezeTaskList, splitRatio, shellInstanceId); - return; - } - - final ActivityOptions options1 = ActivityOptions.makeBasic(); - if (freezeTaskList) { - options1.setFreezeRecentTasksReordering(); - } - boolean hasSecondaryPendingIntent = mSecondPendingIntent != null; - if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) { - final RemoteTransition remoteTransition = getShellRemoteTransition(taskId1, taskId2, - callback); - if (intent1 == null && (intent2 == null && !hasSecondaryPendingIntent)) { - mSystemUiProxy.startTasks(taskId1, options1.toBundle(), taskId2, - null /* options2 */, stagePosition, splitRatio, remoteTransition, - shellInstanceId); - } else if (intent2 == null && !hasSecondaryPendingIntent) { - launchIntentOrShortcut(intent1, mInitialUser, options1, taskId2, stagePosition, - splitRatio, remoteTransition, shellInstanceId); - } else if (intent1 == null) { - launchIntentOrShortcut(intent2, mSecondUser, options1, taskId1, - getOppositeStagePosition(stagePosition), splitRatio, remoteTransition, - shellInstanceId); - } else { - mSystemUiProxy.startIntents(getPendingIntent(intent1, mInitialUser), - mInitialUser.getIdentifier(), getShortcutInfo(intent1, mInitialUser), - options1.toBundle(), hasSecondaryPendingIntent - ? mSecondPendingIntent - : getPendingIntent(intent2, mSecondUser), - mSecondUser.getIdentifier(), getShortcutInfo(intent2, mSecondUser), - null /* options2 */, stagePosition, splitRatio, remoteTransition, - shellInstanceId); - } - } else { - final RemoteAnimationAdapter adapter = getLegacyRemoteAdapter(taskId1, taskId2, - callback); - - if (intent1 == null && (intent2 == null && !hasSecondaryPendingIntent)) { - mSystemUiProxy.startTasksWithLegacyTransition(taskId1, options1.toBundle(), - taskId2, null /* options2 */, stagePosition, splitRatio, adapter, - shellInstanceId); - } else if (intent2 == null && !hasSecondaryPendingIntent) { - launchIntentOrShortcutLegacy(intent1, mInitialUser, options1, taskId2, - stagePosition, splitRatio, adapter, shellInstanceId); - } else if (intent1 == null) { - launchIntentOrShortcutLegacy(intent2, mSecondUser, options1, taskId1, - getOppositeStagePosition(stagePosition), splitRatio, adapter, - shellInstanceId); - } else { - mSystemUiProxy.startIntentsWithLegacyTransition( - getPendingIntent(intent1, mInitialUser), mInitialUser.getIdentifier(), - getShortcutInfo(intent1, mInitialUser), options1.toBundle(), - hasSecondaryPendingIntent - ? mSecondPendingIntent - : getPendingIntent(intent2, mSecondUser), - mSecondUser.getIdentifier(), getShortcutInfo(intent2, mSecondUser), - null /* options2 */, stagePosition, splitRatio, adapter, shellInstanceId); - } - } - } - - private void launchTasksRefactored(Consumer<Boolean> callback, boolean freezeTaskList, - float splitRatio, @Nullable InstanceId shellInstanceId) { final ActivityOptions options1 = ActivityOptions.makeBasic(); if (freezeTaskList) { options1.setFreezeRecentTasksReordering(); @@ -404,7 +344,7 @@ public class SplitSelectStateController { if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) { final RemoteTransition remoteTransition = getShellRemoteTransition(firstTaskId, - secondTaskId, callback); + secondTaskId, callback, "LaunchSplitPair"); switch (launchData.getSplitLaunchType()) { case SPLIT_TASK_TASK -> mSystemUiProxy.startTasks(firstTaskId, optionsBundle, secondTaskId, @@ -477,9 +417,8 @@ public class SplitSelectStateController { /** * Used to launch split screen from a split pair that already exists (usually accessible through - * Overview). This is different than - * {@link #launchTasks(int, Intent, int, Intent, int, Consumer, boolean, float, InstanceId)} in - * that this only launches split screen that are existing tasks. This doesn't determine which + * Overview). This is different than {@link #launchTasks(Consumer, boolean, float, InstanceId)} + * in that this only launches split screen that are existing tasks. This doesn't determine which * API should be used (i.e. launching split with existing tasks vs intents vs shortcuts, etc). * * <p/> @@ -497,7 +436,7 @@ public class SplitSelectStateController { if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) { final RemoteTransition remoteTransition = getShellRemoteTransition(firstTaskId, - secondTaskId, callback); + secondTaskId, callback, "LaunchExistingPair"); mSystemUiProxy.startTasks(firstTaskId, optionsBundle, secondTaskId, null /* options2 */, stagePosition, splitRatio, remoteTransition, null /*shellInstanceId*/); @@ -516,11 +455,6 @@ public class SplitSelectStateController { * split and fullscreen tasks) */ public void launchInitialAppFullscreen(Consumer<Boolean> callback) { - if (!FeatureFlags.ENABLE_SPLIT_LAUNCH_DATA_REFACTOR.get()) { - launchSplitTasks(callback); - return; - } - final ActivityOptions options1 = ActivityOptions.makeBasic(); SplitSelectDataHolder.SplitLaunchData launchData = mSplitSelectDataHolder.getFullscreenLaunchData(); @@ -536,7 +470,7 @@ public class SplitSelectStateController { new RemoteSplitLaunchTransitionRunner(firstTaskId, secondTaskId, callback); final RemoteTransition remoteTransition = new RemoteTransition(animationRunner, ActivityThread.currentActivityThread().getApplicationThread(), - "LaunchSplitPair"); + "LaunchAppFullscreen"); InstanceId instanceId = LogUtils.getShellShareableInstanceId().first; if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) { switch (launchData.getSplitLaunchType()) { @@ -571,111 +505,38 @@ public class SplitSelectStateController { } } + public void initSplitFromDesktopController(Launcher launcher) { + mSplitFromDesktopController = new SplitFromDesktopController(launcher); + } + private RemoteTransition getShellRemoteTransition(int firstTaskId, int secondTaskId, - Consumer<Boolean> callback) { + @Nullable Consumer<Boolean> callback, String transitionName) { final RemoteSplitLaunchTransitionRunner animationRunner = new RemoteSplitLaunchTransitionRunner(firstTaskId, secondTaskId, callback); return new RemoteTransition(animationRunner, - ActivityThread.currentActivityThread().getApplicationThread(), "LaunchSplitPair"); + ActivityThread.currentActivityThread().getApplicationThread(), transitionName); } private RemoteAnimationAdapter getLegacyRemoteAdapter(int firstTaskId, int secondTaskId, - Consumer<Boolean> callback) { + @Nullable Consumer<Boolean> callback) { final RemoteSplitLaunchAnimationRunner animationRunner = new RemoteSplitLaunchAnimationRunner(firstTaskId, secondTaskId, callback); return new RemoteAnimationAdapter(animationRunner, 300, 150, ActivityThread.currentActivityThread().getApplicationThread()); } - private void launchIntentOrShortcut(Intent intent, UserHandle user, ActivityOptions options1, - int taskId, @StagePosition int stagePosition, float splitRatio, - RemoteTransition remoteTransition, @Nullable InstanceId shellInstanceId) { - final ShortcutInfo shortcutInfo = getShortcutInfo(intent, user); - if (shortcutInfo != null) { - mSystemUiProxy.startShortcutAndTask(shortcutInfo, - options1.toBundle(), taskId, null /* options2 */, stagePosition, - splitRatio, remoteTransition, shellInstanceId); - } else { - mSystemUiProxy.startIntentAndTask(getPendingIntent(intent, user), user.getIdentifier(), - options1.toBundle(), taskId, null /* options2 */, stagePosition, splitRatio, - remoteTransition, shellInstanceId); - } - } - - private void launchIntentOrShortcutLegacy(Intent intent, UserHandle user, - ActivityOptions options1, int taskId, @StagePosition int stagePosition, - float splitRatio, RemoteAnimationAdapter adapter, - @Nullable InstanceId shellInstanceId) { - final ShortcutInfo shortcutInfo = getShortcutInfo(intent, user); - if (shortcutInfo != null) { - mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(shortcutInfo, - options1.toBundle(), taskId, null /* options2 */, stagePosition, - splitRatio, adapter, shellInstanceId); - } else { - mSystemUiProxy.startIntentAndTaskWithLegacyTransition( - getPendingIntent(intent, user), user.getIdentifier(), options1.toBundle(), - taskId, null /* options2 */, stagePosition, splitRatio, adapter, - shellInstanceId); - } - } - - /** - * We treat launching by intents as grouped in two ways, - * If {@param intent} represents the first app, we always convert the intent to pending intent - * It it represents second app, either the second intent OR mSecondPendingIntent will be used - * convert second intent to a pendingIntent OR return mSecondPendingIntent as is - */ - private PendingIntent getPendingIntent(Intent intent, UserHandle user) { - boolean isParamFirstIntent = intent != null && intent == mInitialTaskIntent; - if (!isParamFirstIntent && mSecondPendingIntent != null) { - // Because mSecondPendingIntent and mSecondTaskIntent can't both be set, we know we need - // to be using mSecondPendingIntent - return mSecondPendingIntent; - } - - // intent param must either be mInitialTaskIntent or mSecondTaskIntent, convert either to - // a new PendingIntent - return intent == null ? null : (user != null - ? PendingIntent.getActivityAsUser(mContext, 0, intent, - FLAG_MUTABLE | FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT, null /* options */, user) - : PendingIntent.getActivity(mContext, 0, intent, - FLAG_MUTABLE | FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT)); - } - public @StagePosition int getActiveSplitStagePosition() { - return mInitialStagePosition; + return mSplitSelectDataHolder.getInitialStagePosition(); } public StatsLogManager.EventEnum getSplitEvent() { - return mSplitEvent; + return mSplitSelectDataHolder.getSplitEvent(); } public void setRecentsAnimationRunning(boolean running) { mRecentsAnimationRunning = running; } - @Nullable - private ShortcutInfo getShortcutInfo(Intent intent, UserHandle user) { - if (intent == null || intent.getPackage() == null) { - return null; - } - - final String shortcutId = intent.getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID); - if (shortcutId == null) { - return null; - } - - try { - final Context context = mContext.createPackageContextAsUser( - intent.getPackage(), 0 /* flags */, user); - return new ShortcutInfo.Builder(context, shortcutId).build(); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Failed to create a ShortcutInfo for " + intent.getPackage()); - } - - return null; - } - public boolean isAnimateCurrentTaskDismissal() { return mAnimateCurrentTaskDismissal; } @@ -706,7 +567,7 @@ public class SplitSelectStateController { private final Consumer<Boolean> mSuccessCallback; RemoteSplitLaunchTransitionRunner(int initialTaskId, int secondTaskId, - Consumer<Boolean> callback) { + @Nullable Consumer<Boolean> callback) { mInitialTaskId = initialTaskId; mSecondTaskId = secondTaskId; mSuccessCallback = callback; @@ -716,6 +577,7 @@ public class SplitSelectStateController { public void startAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishedCallback) { + testLogD(LAUNCH_SPLIT_PAIR, "Received split startAnimation"); final Runnable finishAdapter = () -> { try { finishedCallback.onTransitionFinished(null /* wct */, null /* sct */); @@ -731,9 +593,8 @@ public class SplitSelectStateController { if (mSuccessCallback != null) { mSuccessCallback.accept(true); } + resetState(); }); - // After successful launch, call resetState - resetState(); }); } @@ -754,7 +615,7 @@ public class SplitSelectStateController { private final Consumer<Boolean> mSuccessCallback; RemoteSplitLaunchAnimationRunner(int initialTaskId, int secondTaskId, - Consumer<Boolean> successCallback) { + @Nullable Consumer<Boolean> successCallback) { mInitialTaskId = initialTaskId; mSecondTaskId = secondTaskId; mSuccessCallback = successCallback; @@ -790,26 +651,20 @@ public class SplitSelectStateController { } /** - * To be called if split select was cancelled + * To be called whenever we exit split selection state. If + * {@link FeatureFlags#ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE} is set, this should be the + * central way split is getting reset, which should then go through the callbacks to reset + * other state. */ public void resetState() { - if (FeatureFlags.ENABLE_SPLIT_LAUNCH_DATA_REFACTOR.get()) { - mSplitSelectDataHolder.resetState(); - } - mInitialTaskId = INVALID_TASK_ID; - mInitialTaskIntent = null; - mSecondTaskId = INVALID_TASK_ID; - mSecondTaskIntent = null; - mInitialUser = null; - mSecondUser = null; - mInitialStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED; + mSplitSelectDataHolder.resetState(); + dispatchOnSplitSelectionExit(); mRecentsAnimationRunning = false; mLaunchingTaskView = null; - mItemInfo = null; - mSplitEvent = null; mAnimateCurrentTaskDismissal = false; mDismissingFromSplitPair = false; - mSecondPendingIntent = null; + mFirstFloatingTaskView = null; + mSplitInstructionsView = null; } /** @@ -817,11 +672,7 @@ public class SplitSelectStateController { * chosen */ public boolean isSplitSelectActive() { - if (FeatureFlags.ENABLE_SPLIT_LAUNCH_DATA_REFACTOR.get()) { - return mSplitSelectDataHolder.isSplitSelectActive(); - } else { - return isInitialTaskIntentSet() && !isSecondTaskIntentSet(); - } + return mSplitSelectDataHolder.isSplitSelectActive(); } /** @@ -829,46 +680,35 @@ public class SplitSelectStateController { * be launched */ public boolean isBothSplitAppsConfirmed() { - if (FeatureFlags.ENABLE_SPLIT_LAUNCH_DATA_REFACTOR.get()) { - return mSplitSelectDataHolder.isBothSplitAppsConfirmed(); - } else { - return isInitialTaskIntentSet() && isSecondTaskIntentSet(); - } - } - - private boolean isInitialTaskIntentSet() { - return (mInitialTaskId != INVALID_TASK_ID || mInitialTaskIntent != null); + return mSplitSelectDataHolder.isBothSplitAppsConfirmed(); } public int getInitialTaskId() { - if (FeatureFlags.ENABLE_SPLIT_LAUNCH_DATA_REFACTOR.get()) { - return mSplitSelectDataHolder.getInitialTaskId(); - } else { - return mInitialTaskId; - } + return mSplitSelectDataHolder.getInitialTaskId(); } public int getSecondTaskId() { - if (FeatureFlags.ENABLE_SPLIT_LAUNCH_DATA_REFACTOR.get()) { - return mSplitSelectDataHolder.getSecondTaskId(); - } else { - return mSecondTaskId; - } - } - - private boolean isSecondTaskIntentSet() { - return (mSecondTaskId != INVALID_TASK_ID || mSecondTaskIntent != null - || mSecondPendingIntent != null); + return mSplitSelectDataHolder.getSecondTaskId(); } public void setFirstFloatingTaskView(FloatingTaskView floatingTaskView) { mFirstFloatingTaskView = floatingTaskView; } + public void setSplitInstructionsView(SplitInstructionsView splitInstructionsView) { + mSplitInstructionsView = splitInstructionsView; + } + + @Nullable public FloatingTaskView getFirstFloatingTaskView() { return mFirstFloatingTaskView; } + @Nullable + public SplitInstructionsView getSplitInstructionsView() { + return mSplitInstructionsView; + } + public AppPairsController getAppPairsController() { return mAppPairsController; } @@ -878,4 +718,128 @@ public class SplitSelectStateController { mSplitSelectDataHolder.dump(prefix, writer); } } + + public class SplitFromDesktopController { + private static final String TAG = "SplitFromDesktopController"; + + private final Launcher mLauncher; + private final OverviewComponentObserver mOverviewComponentObserver; + private final int mSplitPlaceholderSize; + private final int mSplitPlaceholderInset; + private ActivityManager.RunningTaskInfo mTaskInfo; + private ISplitSelectListener mSplitSelectListener; + private Drawable mAppIcon; + + public SplitFromDesktopController(Launcher launcher) { + mLauncher = launcher; + RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState( + launcher.getApplicationContext()); + mOverviewComponentObserver = + new OverviewComponentObserver(launcher.getApplicationContext(), deviceState); + mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize( + R.dimen.split_placeholder_size); + mSplitPlaceholderInset = mLauncher.getResources().getDimensionPixelSize( + R.dimen.split_placeholder_inset); + mSplitSelectListener = new ISplitSelectListener.Stub() { + @Override + public boolean onRequestSplitSelect(ActivityManager.RunningTaskInfo taskInfo, + int splitPosition, Rect taskBounds) { + if (!ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE.get()) return false; + MAIN_EXECUTOR.execute(() -> enterSplitSelect(taskInfo, splitPosition, + taskBounds)); + return true; + } + }; + SystemUiProxy.INSTANCE.get(mLauncher).registerSplitSelectListener(mSplitSelectListener); + } + + /** + * Enter split select from desktop mode. + * @param taskInfo the desktop task to move to split stage + * @param splitPosition the stage position used for this transition + * @param taskBounds the bounds of the task, used for {@link FloatingTaskView} animation + */ + public void enterSplitSelect(ActivityManager.RunningTaskInfo taskInfo, + int splitPosition, Rect taskBounds) { + mTaskInfo = taskInfo; + String packageName = mTaskInfo.realActivity.getPackageName(); + PackageManager pm = mLauncher.getApplicationContext().getPackageManager(); + IconProvider provider = new IconProvider(mLauncher.getApplicationContext()); + try { + mAppIcon = provider.getIcon(pm.getActivityInfo(mTaskInfo.baseActivity, + PackageManager.ComponentInfoFlags.of(0))); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Package not found: " + packageName, e); + } + RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks( + SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()), + false /* allowMinimizeSplitScreen */); + + DesktopSplitRecentsAnimationListener listener = + new DesktopSplitRecentsAnimationListener(splitPosition, taskBounds); + + MAIN_EXECUTOR.execute(() -> { + callbacks.addListener(listener); + UI_HELPER_EXECUTOR.execute( + // Transition from app to enter stage split in launcher with + // recents animation. + () -> ActivityManagerWrapper.getInstance().startRecentsActivity( + mOverviewComponentObserver.getOverviewIntent(), + SystemClock.uptimeMillis(), callbacks, null, null)); + }); + } + + private class DesktopSplitRecentsAnimationListener implements + RecentsAnimationCallbacks.RecentsAnimationListener { + private final Rect mTempRect = new Rect(); + private final RectF mTaskBounds = new RectF(); + private final int mSplitPosition; + + DesktopSplitRecentsAnimationListener(int splitPosition, Rect taskBounds) { + mSplitPosition = splitPosition; + mTaskBounds.set(taskBounds); + } + + @Override + public void onRecentsAnimationStart(RecentsAnimationController controller, + RecentsAnimationTargets targets) { + StatsLogManager.LauncherEvent launcherDesktopSplitEvent = + mSplitPosition == STAGE_POSITION_BOTTOM_OR_RIGHT ? + LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM : + LAUNCHER_DESKTOP_MODE_SPLIT_LEFT_TOP; + setInitialTaskSelect(mTaskInfo, mSplitPosition, + null, launcherDesktopSplitEvent); + + RecentsView recentsView = mLauncher.getOverviewPanel(); + recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds( + mSplitPlaceholderSize, mSplitPlaceholderInset, + mLauncher.getDeviceProfile(), getActiveSplitStagePosition(), mTempRect); + + PendingAnimation anim = new PendingAnimation( + SplitAnimationTimings.TABLET_HOME_TO_SPLIT.getDuration()); + final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView( + mLauncher, mLauncher.getDragLayer(), + null /* thumbnail */, + mAppIcon, new RectF()); + floatingTaskView.setAlpha(1); + floatingTaskView.addStagingAnimation(anim, mTaskBounds, mTempRect, + false /* fadeWithThumbnail */, true /* isStagedTask */); + setFirstFloatingTaskView(floatingTaskView); + + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + controller.finish(true /* toRecents */, null /* onFinishComplete */, + false /* sendUserLeaveHint */); + } + @Override + public void onAnimationEnd(Animator animation) { + SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()) + .onDesktopSplitSelectAnimComplete(mTaskInfo); + } + }); + anim.buildAnim().start(); + } + } + } } diff --git a/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java b/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java index f5b00cf42b..d1ec2b6f23 100644 --- a/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java +++ b/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java @@ -16,7 +16,7 @@ package com.android.quickstep.util; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED; +import static com.android.app.animation.Interpolators.EMPHASIZED; import android.view.animation.Interpolator; diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java index 148a45a386..056f9aa4a7 100644 --- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java +++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java @@ -16,6 +16,7 @@ package com.android.quickstep.util; +import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE; import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS; import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE; @@ -169,6 +170,7 @@ public class SplitToWorkspaceController { private void cleanUp() { mLauncher.getDragLayer().removeView(firstFloatingTaskView); mLauncher.getDragLayer().removeView(secondFloatingTaskView); + mController.getSplitAnimationController().removeSplitInstructionsView(mLauncher); mController.resetState(); } }); @@ -177,7 +179,8 @@ public class SplitToWorkspaceController { private boolean shouldIgnoreSecondSplitLaunch() { return (!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get() - && !ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) + && !ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get() + && !ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE.get()) || !mController.isSplitSelectActive(); } } diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java index 24d832640f..f3fa86a86c 100644 --- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java +++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java @@ -27,21 +27,15 @@ import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITIO import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.ActivityManager; -import android.content.Intent; import android.graphics.Rect; import android.graphics.RectF; import android.os.SystemClock; -import android.os.UserHandle; -import android.view.View; import androidx.annotation.BinderThread; -import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.anim.PendingAnimation; -import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.uioverrides.QuickstepLauncher; -import com.android.quickstep.OverviewCommandHelper; import com.android.quickstep.OverviewComponentObserver; import com.android.quickstep.RecentsAnimationCallbacks; import com.android.quickstep.RecentsAnimationController; @@ -143,11 +137,7 @@ public class SplitWithKeyboardShortcutController { .updateIconInBackground( Task.from(new Task.TaskKey(runningTaskInfo), runningTaskInfo, false /* isLocked */), - (task) -> { - if (task.thumbnail != null) { - floatingTaskView.setIcon(task.thumbnail.thumbnail); - } - }); + (task) -> floatingTaskView.setIcon(task.icon)); floatingTaskView.setAlpha(1); floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect, false /* fadeWithThumbnail */, true /* isStagedTask */); diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java index cd5edab9d6..6b3199f5b4 100644 --- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java +++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java @@ -15,11 +15,11 @@ */ package com.android.quickstep.util; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER; import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; @@ -177,8 +177,8 @@ public class StaggeredWorkspaceAnim { addDepthAnimationForState(launcher, NORMAL, duration); - mAnimators.play(launcher.getRootView().getSysUiScrim().createSysuiMultiplierAnim(0f, 1f) - .setDuration(duration)); + mAnimators.play(launcher.getRootView().getSysUiScrim().getSysUIMultiplier() + .animateToValue(0f, 1f).setDuration(duration)); } private void addAnimationForPage(CellLayout page, int totalRows, long duration) { diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java index 441f88d115..5fd86c036f 100644 --- a/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java +++ b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java @@ -166,5 +166,10 @@ public class SurfaceTransaction { this.shadowRadius = radius; return this; } + + @Override + public SurfaceProperties setShow() { + return this; + } } } diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java index 1112f4dd9e..7cc2c468fa 100644 --- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java +++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java @@ -26,7 +26,6 @@ import android.content.pm.ActivityInfo; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; -import android.os.SystemProperties; import android.util.Log; import android.view.Surface; import android.view.SurfaceControl; @@ -153,14 +152,9 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { // Create a new overlay layer. We do not call detach on this instance, it's propagated // to other classes like PipTaskOrganizer / RecentsAnimationController to complete // the cleanup. - if (SystemProperties.getBoolean( - "persist.wm.debug.enable_pip_app_icon_overlay", true)) { - mPipContentOverlay = new PipContentOverlay.PipAppIconOverlay(view.getContext(), - mAppBounds, new IconProvider(context).getIcon(mActivityInfo), - appIconSizePx); - } else { - mPipContentOverlay = new PipContentOverlay.PipColorOverlay(view.getContext()); - } + mPipContentOverlay = new PipContentOverlay.PipAppIconOverlay(view.getContext(), + mAppBounds, new IconProvider(context).getIcon(mActivityInfo), + appIconSizePx); final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); mPipContentOverlay.attach(tx, mLeash); } else { diff --git a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java index 9808b2877c..c82cdb7c3c 100644 --- a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java +++ b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java @@ -24,11 +24,11 @@ import android.view.WindowManager; import android.view.WindowMetrics; import com.android.internal.policy.SystemBarUtils; -import com.android.launcher3.logging.FileLog; import com.android.launcher3.util.WindowBounds; import com.android.launcher3.util.window.CachedDisplayInfo; import com.android.launcher3.util.window.WindowManagerProxy; +import java.util.List; import java.util.Set; /** @@ -54,16 +54,15 @@ public class SystemWindowManagerProxy extends WindowManagerProxy { } @Override - public ArrayMap<CachedDisplayInfo, WindowBounds[]> estimateInternalDisplayBounds( + public ArrayMap<CachedDisplayInfo, List<WindowBounds>> estimateInternalDisplayBounds( Context displayInfoContext) { - ArrayMap<CachedDisplayInfo, WindowBounds[]> result = new ArrayMap<>(); + ArrayMap<CachedDisplayInfo, List<WindowBounds>> result = new ArrayMap<>(); WindowManager windowManager = displayInfoContext.getSystemService(WindowManager.class); Set<WindowMetrics> possibleMaximumWindowMetrics = windowManager.getPossibleMaximumWindowMetrics(DEFAULT_DISPLAY); - FileLog.d("b/283944974", "possibleMaximumWindowMetrics: " + possibleMaximumWindowMetrics); for (WindowMetrics windowMetrics : possibleMaximumWindowMetrics) { CachedDisplayInfo info = getDisplayInfo(windowMetrics, Surface.ROTATION_0); - WindowBounds[] bounds = estimateWindowBounds(displayInfoContext, info); + List<WindowBounds> bounds = estimateWindowBounds(displayInfoContext, info); result.put(info, bounds); } return result; diff --git a/quickstep/src/com/android/quickstep/util/TISBindHelper.java b/quickstep/src/com/android/quickstep/util/TISBindHelper.java index 7b122c6c66..ddc796fc33 100644 --- a/quickstep/src/com/android/quickstep/util/TISBindHelper.java +++ b/quickstep/src/com/android/quickstep/util/TISBindHelper.java @@ -23,6 +23,10 @@ import android.os.Handler; import android.os.IBinder; import android.util.Log; +import androidx.annotation.Nullable; + +import com.android.launcher3.taskbar.TaskbarManager; +import com.android.quickstep.OverviewCommandHelper; import com.android.quickstep.TouchInteractionService; import com.android.quickstep.TouchInteractionService.TISBinder; @@ -50,6 +54,7 @@ public class TISBindHelper implements ServiceConnection { private short mConnectionAttempts; private boolean mTisServiceBound; private boolean mIsConnected; + @Nullable private TISBinder mBinder; public TISBindHelper(Context context, Consumer<TISBinder> connectionCallback) { mContext = context; @@ -70,7 +75,8 @@ public class TISBindHelper implements ServiceConnection { Log.d(TAG, "TIS service connected"); mIsConnected = true; - mConnectionCallback.accept((TISBinder) iBinder); + mBinder = (TISBinder) iBinder; + mConnectionCallback.accept(mBinder); // Flush the pending callbacks for (Runnable r : mPendingConnectedCallbacks) { r.run(); @@ -80,7 +86,11 @@ public class TISBindHelper implements ServiceConnection { } @Override - public void onServiceDisconnected(ComponentName componentName) { } + public void onServiceDisconnected(ComponentName componentName) { + Log.d(TAG, "TIS service disconnected"); + mBinder = null; + mIsConnected = false; + } @Override public void onBindingDied(ComponentName name) { @@ -88,6 +98,21 @@ public class TISBindHelper implements ServiceConnection { internalBindToTIS(); } + @Nullable + public TISBinder getBinder() { + return mBinder; + } + + @Nullable + public TaskbarManager getTaskbarManager() { + return mBinder == null ? null : mBinder.getTaskbarManager(); + } + + @Nullable + public OverviewCommandHelper getOverviewCommandHelper() { + return mBinder == null ? null : mBinder.getOverviewCommandHelper(); + } + /** * Runs the given {@param r} runnable when the service is connected. */ @@ -139,6 +164,7 @@ public class TISBindHelper implements ServiceConnection { public void onDestroy() { internalUnbindToTIS(); resetServiceBindRetryState(); + mBinder = null; mIsConnected = false; mPendingConnectedCallbacks.clear(); } diff --git a/quickstep/src/com/android/quickstep/util/TabletHomeToSplitTimings.java b/quickstep/src/com/android/quickstep/util/TabletHomeToSplitTimings.java index bf8612a797..8804049550 100644 --- a/quickstep/src/com/android/quickstep/util/TabletHomeToSplitTimings.java +++ b/quickstep/src/com/android/quickstep/util/TabletHomeToSplitTimings.java @@ -16,7 +16,7 @@ package com.android.quickstep.util; -import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.LINEAR; import android.view.animation.Interpolator; diff --git a/quickstep/src/com/android/quickstep/util/TabletOverviewToSplitTimings.java b/quickstep/src/com/android/quickstep/util/TabletOverviewToSplitTimings.java index cbf46bfcb8..5463d84b65 100644 --- a/quickstep/src/com/android/quickstep/util/TabletOverviewToSplitTimings.java +++ b/quickstep/src/com/android/quickstep/util/TabletOverviewToSplitTimings.java @@ -16,7 +16,7 @@ package com.android.quickstep.util; -import static com.android.launcher3.anim.Interpolators.DEACCEL_2; +import static com.android.app.animation.Interpolators.DECELERATE_2; import android.view.animation.Interpolator; @@ -36,8 +36,8 @@ public class TabletOverviewToSplitTimings public int getGridSlideDuration() { return 500; } public int getDuration() { return TABLET_ENTER_DURATION; } - public Interpolator getStagedRectXInterpolator() { return DEACCEL_2; } - public Interpolator getStagedRectYInterpolator() { return DEACCEL_2; } - public Interpolator getStagedRectScaleXInterpolator() { return DEACCEL_2; } - public Interpolator getStagedRectScaleYInterpolator() { return DEACCEL_2; } + public Interpolator getStagedRectXInterpolator() { return DECELERATE_2; } + public Interpolator getStagedRectYInterpolator() { return DECELERATE_2; } + public Interpolator getStagedRectScaleXInterpolator() { return DECELERATE_2; } + public Interpolator getStagedRectScaleYInterpolator() { return DECELERATE_2; } } diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java index 303a5283d8..9b2a449337 100644 --- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java @@ -112,8 +112,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mContext = context; mSizeStrategy = sizeStrategy; - // TODO(b/187074722): Don't create this per-TaskViewSimulator - mOrientationState = TraceHelper.allowIpcs("", + mOrientationState = TraceHelper.allowIpcs("TaskViewSimulator.init", () -> new RecentsOrientedState(context, sizeStrategy, i -> { })); mOrientationState.setGestureActive(true); mCurrentFullscreenParams = new FullscreenDrawParams(context); @@ -376,6 +375,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mInversePositionMatrix.mapRect(mTempRectF); mTempRectF.roundOut(mTmpCropRect); + params.setProgress(1f - fullScreenProgress); params.applySurfaceParams(params.createSurfaceParams(this)); if (!DEBUG) { diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java index 0f20e430fc..ca680db2d3 100644 --- a/quickstep/src/com/android/quickstep/util/TransformParams.java +++ b/quickstep/src/com/android/quickstep/util/TransformParams.java @@ -15,14 +15,14 @@ */ package com.android.quickstep.util; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import android.util.FloatProperty; import android.view.RemoteAnimationTarget; +import com.android.app.animation.Interpolators; import com.android.launcher3.Utilities; -import com.android.launcher3.anim.Interpolators; import com.android.quickstep.RemoteAnimationTargets; import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties; @@ -54,6 +54,7 @@ public class TransformParams { } }; + /** Progress from 0 to 1 where 0 is in-app and 1 is Overview */ private float mProgress; private float mTargetAlpha; private float mCornerRadius; @@ -135,6 +136,7 @@ public class TransformParams { return this; } + /** Builds the SurfaceTransaction from the given BuilderProxy params. */ public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) { RemoteAnimationTargets targets = mTargetSet; SurfaceTransaction transaction = new SurfaceTransaction(); @@ -150,10 +152,15 @@ public class TransformParams { if (activityType == ACTIVITY_TYPE_HOME) { mHomeBuilderProxy.onBuildTargetParams(builder, app, this); } else { - // Fade out Assistant overlay. - if (activityType == ACTIVITY_TYPE_ASSISTANT && app.isNotInRecents) { + // Fade out translucent overlay. + // TODO(b/303351074): use app.isNotInRecents directly once it is fixed. + boolean isNotInRecents = app.taskInfo != null + && (app.taskInfo.baseIntent.getFlags() + & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0; + if (app.isTranslucent && isNotInRecents) { float progress = Utilities.boundToRange(getProgress(), 0, 1); - builder.setAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress)); + builder.setAlpha(1 - Interpolators.DECELERATE_QUINT + .getInterpolation(progress)); } else { builder.setAlpha(getTargetAlpha()); } diff --git a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java index 34fa7f1764..0a97793b28 100644 --- a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java +++ b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java @@ -32,11 +32,11 @@ import android.animation.ObjectAnimator; import android.util.FloatProperty; import android.view.View; +import com.android.app.animation.Interpolators; import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Workspace; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.states.StateAnimationConfig; @@ -92,7 +92,8 @@ public class WorkspaceRevealAnim { } // Add sysui scrim animation. - mAnimators.play(launcher.getRootView().getSysUiScrim().createSysuiMultiplierAnim(0f, 1f)); + mAnimators.play(launcher.getRootView().getSysUiScrim() + .getSysUIMultiplier().animateToValue(0f, 1f)); mAnimators.setDuration(DURATION_MS); mAnimators.setInterpolator(Interpolators.DECELERATED_EASE); diff --git a/quickstep/src/com/android/quickstep/views/AllAppsEduView.java b/quickstep/src/com/android/quickstep/views/AllAppsEduView.java index 716d389a2c..fdc8f1ff14 100644 --- a/quickstep/src/com/android/quickstep/views/AllAppsEduView.java +++ b/quickstep/src/com/android/quickstep/views/AllAppsEduView.java @@ -15,12 +15,12 @@ */ package com.android.quickstep.views; +import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.OVERSHOOT_1_7; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALL_APPS_EDU_SHOWN; import android.animation.Animator; diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java index 6813857ba9..19ac1f8eda 100644 --- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java +++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java @@ -16,6 +16,8 @@ package com.android.quickstep.views; +import static com.android.launcher3.config.FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW; + import android.content.Context; import android.util.AttributeSet; import android.util.FloatProperty; @@ -248,8 +250,15 @@ public class ClearAllButton extends Button { */ private float getOriginalTranslationY() { DeviceProfile deviceProfile = mActivity.getDeviceProfile(); - return deviceProfile.isTablet - ? deviceProfile.overviewRowSpacing - : deviceProfile.overviewTaskThumbnailTopMarginPx / 2.0f; + if (deviceProfile.isTablet) { + if (ENABLE_GRID_ONLY_OVERVIEW.get()) { + return (getRecentsView().getLastComputedTaskSize().height() + + deviceProfile.overviewTaskThumbnailTopMarginPx) / 2.0f + + deviceProfile.overviewRowSpacing; + } else { + return deviceProfile.overviewRowSpacing; + } + } + return deviceProfile.overviewTaskThumbnailTopMarginPx / 2.0f; } } diff --git a/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java b/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java new file mode 100644 index 0000000000..a5be142235 --- /dev/null +++ b/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java @@ -0,0 +1,179 @@ +/* + * 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.quickstep.views; + +import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE; +import static com.android.app.animation.Interpolators.LINEAR; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; + +import androidx.annotation.Nullable; + +import com.android.launcher3.Launcher; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; + +/** + * Floating view show on launcher home screen that notifies the user that an app will be launched to + * the desktop. + */ +public class DesktopAppSelectView extends LinearLayout { + + private static final int SHOW_INITIAL_HEIGHT_DP = 7; + private static final int SHOW_CONTAINER_SCALE_DURATION = 333; + private static final int SHOW_CONTAINER_ALPHA_DURATION = 83; + private static final int SHOW_CONTENT_ALPHA_DELAY = 67; + private static final int SHOW_CONTENT_ALPHA_DURATION = 83; + private static final int HIDE_DURATION = 83; + + private final Launcher mLauncher; + + private View mText; + private View mCloseButton; + @Nullable + private Runnable mOnCloseCallback; + private AnimatorSet mShowAnimation; + private Animator mHideAnimation; + + public DesktopAppSelectView(Context context) { + this(context, null); + } + + public DesktopAppSelectView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public DesktopAppSelectView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public DesktopAppSelectView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + mLauncher = Launcher.getLauncher(context); + } + + /** + * Show the popup on launcher home screen + * + * @param onCloseCallback optional callback that is called when user clicks the close button + * @return the created view + */ + public static DesktopAppSelectView show(Launcher launcher, @Nullable Runnable onCloseCallback) { + DesktopAppSelectView view = (DesktopAppSelectView) launcher.getLayoutInflater().inflate( + R.layout.floating_desktop_app_select, launcher.getDragLayer(), false); + view.setOnCloseClickCallback(onCloseCallback); + view.show(); + return view; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mText = findViewById(R.id.desktop_app_select_text); + mCloseButton = findViewById(R.id.close_button); + mCloseButton.setOnClickListener(v -> { + if (mHideAnimation == null) { + hide(); + if (mOnCloseCallback != null) { + mOnCloseCallback.run(); + } + } + }); + } + + private void show() { + mLauncher.getDragLayer().addView(this); + + // Set up initial values + getBackground().setAlpha(0); + mText.setAlpha(0); + mCloseButton.setAlpha(0); + int initialHeightPx = Utilities.dpToPx(SHOW_INITIAL_HEIGHT_DP); + int finalHeight = getResources().getDimensionPixelSize( + R.dimen.desktop_mode_floating_app_select_height); + float initialScale = initialHeightPx / (float) finalHeight; + setScaleY(initialScale); + setPivotY(0); + + // Animate the container + ValueAnimator containerBackground = ValueAnimator.ofInt(0, 255); + containerBackground.addUpdateListener( + animation -> getBackground().setAlpha((Integer) animation.getAnimatedValue())); + containerBackground.setDuration(SHOW_CONTAINER_ALPHA_DURATION); + containerBackground.setInterpolator(LINEAR); + + ObjectAnimator containerSize = ObjectAnimator.ofFloat(this, SCALE_Y, 1f); + containerSize.setDuration(SHOW_CONTAINER_SCALE_DURATION); + containerSize.setInterpolator(EMPHASIZED_DECELERATE); + + // Animate the contents + ObjectAnimator textAlpha = ObjectAnimator.ofFloat(mText, ALPHA, 1); + ObjectAnimator buttonAlpha = ObjectAnimator.ofFloat(mCloseButton, ALPHA, 1); + AnimatorSet contentAlpha = new AnimatorSet(); + contentAlpha.playTogether(textAlpha, buttonAlpha); + contentAlpha.setStartDelay(SHOW_CONTENT_ALPHA_DELAY); + contentAlpha.setDuration(SHOW_CONTENT_ALPHA_DURATION); + contentAlpha.setInterpolator(LINEAR); + + // Start the animation + mShowAnimation = new AnimatorSet(); + mShowAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + mShowAnimation = null; + } + }); + mShowAnimation.playTogether(containerBackground, containerSize, contentAlpha); + mShowAnimation.start(); + } + + /** + * Hide the floating view + */ + public void hide() { + if (mShowAnimation != null) { + mShowAnimation.cancel(); + } + mHideAnimation = ObjectAnimator.ofFloat(this, ALPHA, 0); + mHideAnimation.setDuration(HIDE_DURATION).setInterpolator(LINEAR); + mHideAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + mLauncher.getDragLayer().removeView(DesktopAppSelectView.this); + mHideAnimation = null; + } + }); + mHideAnimation.start(); + } + + /** + * Add a callback that is called when close button is clicked + */ + public void setOnCloseClickCallback(@Nullable Runnable callback) { + mOnCloseCallback = callback; + } +} diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java index 1cfaf14224..dfd4390203 100644 --- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java +++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java @@ -22,6 +22,7 @@ import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED; import android.content.Context; +import android.content.pm.PackageManager; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -43,6 +44,7 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.icons.IconProvider; import com.android.launcher3.util.RunnableList; import com.android.quickstep.RecentsModel; import com.android.quickstep.SystemUiProxy; @@ -51,6 +53,7 @@ import com.android.quickstep.util.CancellableTask; import com.android.quickstep.util.RecentsOrientedState; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; +import com.android.systemui.shared.system.QuickStepContract; import java.util.ArrayList; import java.util.Arrays; @@ -79,7 +82,7 @@ public class DesktopTaskView extends TaskView { private static final String TAG = DesktopTaskView.class.getSimpleName(); - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; @NonNull private List<Task> mTasks = new ArrayList<>(); @@ -91,6 +94,8 @@ public class DesktopTaskView extends TaskView { private final ArrayList<CancellableTask<?>> mPendingThumbnailRequests = new ArrayList<>(); + private final TaskView.FullscreenDrawParams mSnapshotDrawParams; + private View mBackgroundView; public DesktopTaskView(Context context) { @@ -103,6 +108,18 @@ public class DesktopTaskView extends TaskView { public DesktopTaskView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); + + mSnapshotDrawParams = new FullscreenDrawParams(context) { + @Override + public float computeTaskCornerRadius(Context context) { + return QuickStepContract.getWindowCornerRadius(context); + } + + @Override + public float computeWindowCornerRadius(Context context) { + return QuickStepContract.getWindowCornerRadius(context); + } + }; } @Override @@ -213,7 +230,22 @@ public class DesktopTaskView extends TaskView { private TaskIdAttributeContainer createAttributeContainer(Task task, TaskThumbnailView thumbnailView) { - return new TaskIdAttributeContainer(task, thumbnailView, null, STAGE_POSITION_UNDEFINED); + return new TaskIdAttributeContainer(task, thumbnailView, createIconView(task), + STAGE_POSITION_UNDEFINED); + } + + private IconView createIconView(Task task) { + IconView iconView = new IconView(mContext); + PackageManager pm = mContext.getApplicationContext().getPackageManager(); + try { + IconProvider provider = new IconProvider(mContext); + Drawable appIcon = provider.getIcon(pm.getActivityInfo(task.topActivity, + PackageManager.ComponentInfoFlags.of(0))); + iconView.setDrawable(appIcon); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Package not found: " + task.topActivity.getPackageName(), e); + } + return iconView; } @Nullable @@ -465,14 +497,20 @@ public class DesktopTaskView extends TaskView { for (int i = 0; i < mSnapshotViewMap.size(); i++) { TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i); thumbnailView.getTaskOverlay().setFullscreenProgress(progress); - updateSnapshotRadius(); } + updateSnapshotRadius(); } @Override protected void updateSnapshotRadius() { + super.updateSnapshotRadius(); for (int i = 0; i < mSnapshotViewMap.size(); i++) { - mSnapshotViewMap.valueAt(i).setFullscreenParams(mCurrentFullscreenParams); + if (i == 0) { + // All snapshots share the same params. Only update it with the first snapshot. + updateFullscreenParams(mSnapshotDrawParams, + mSnapshotView.getPreviewPositionHelper()); + } + mSnapshotViewMap.valueAt(i).setFullscreenParams(mSnapshotDrawParams); } } diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java index 7bbe36a0ed..e5a0e10e41 100644 --- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java +++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java @@ -145,23 +145,29 @@ public final class DigitalWellBeingToast { mAppUsageLimitTimeMs = mAppRemainingTimeMs = -1; mTask = task; THREAD_POOL_EXECUTOR.execute(() -> { - final AppUsageLimit usageLimit = mLauncherApps.getAppUsageLimit( - mTask.getTopComponent().getPackageName(), - UserHandle.of(mTask.key.userId)); - - final long appUsageLimitTimeMs = - usageLimit != null ? usageLimit.getTotalUsageLimit() : -1; - final long appRemainingTimeMs = - usageLimit != null ? usageLimit.getUsageRemaining() : -1; - - mTaskView.post(() -> { - if (appUsageLimitTimeMs < 0 || appRemainingTimeMs < 0) { - setNoLimit(); - } else { - setLimit(appUsageLimitTimeMs, appRemainingTimeMs); + AppUsageLimit usageLimit = null; + try { + usageLimit = mLauncherApps.getAppUsageLimit( + mTask.getTopComponent().getPackageName(), + UserHandle.of(mTask.key.userId)); + } catch (Exception e) { + Log.e(TAG, "Error initializing digital well being toast", e); + } + final long appUsageLimitTimeMs = + usageLimit != null ? usageLimit.getTotalUsageLimit() : -1; + final long appRemainingTimeMs = + usageLimit != null ? usageLimit.getUsageRemaining() : -1; + + mTaskView.post(() -> { + if (appUsageLimitTimeMs < 0 || appRemainingTimeMs < 0) { + setNoLimit(); + } else { + setLimit(appUsageLimitTimeMs, appRemainingTimeMs); + } + }); + } - }); - }); + ); } public void setSplitConfiguration(SplitBounds splitBounds) { diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java index 75a8ea2b3b..f250b8c510 100644 --- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java +++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java @@ -1,8 +1,8 @@ package com.android.quickstep.views; +import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.clampToProgress; import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.clampToProgress; import android.animation.ValueAnimator; import android.content.Context; @@ -11,7 +11,6 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.FloatProperty; @@ -213,8 +212,8 @@ public class FloatingTaskView extends FrameLayout { mSplitPlaceholderView.getIconView().setRotation(mOrientationHandler.getDegreesRotated()); } - public void setIcon(Bitmap icon) { - mSplitPlaceholderView.setIcon(new BitmapDrawable(icon), mSplitHolderSize); + public void setIcon(Drawable drawable) { + mSplitPlaceholderView.setIcon(drawable, mSplitHolderSize); } protected void initPosition(RectF pos, InsettableFrameLayout.LayoutParams lp) { diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java index c91b18347c..01f6ae8d5c 100644 --- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java +++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java @@ -223,11 +223,12 @@ public class GroupedTaskView extends TaskView { // Callbacks run from remote animation when recents animation not currently running InteractionJankMonitorWrapper.begin(this, InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Enter form GroupedTaskView"); - launchTask(success -> { + launchTaskInternal(success -> { endCallback.executeAllAndDestroy(); InteractionJankMonitorWrapper.end( InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER); - }, false /* freezeTaskList */); + }, false /* freezeTaskList */, true /*launchingExistingTaskview*/); + // Callbacks get run from recentsView for case when recents animation already running recentsView.addSideTaskLaunchCallback(endCallback); @@ -236,7 +237,19 @@ public class GroupedTaskView extends TaskView { @Override public void launchTask(@NonNull Consumer<Boolean> callback, boolean isQuickswitch) { - getRecentsView().getSplitSelectController().launchExistingSplitPair(this, mTask.key.id, + launchTaskInternal(callback, isQuickswitch, false /*launchingExistingTaskview*/); + } + + /** + * @param launchingExistingTaskView {@link SplitSelectStateController#launchExistingSplitPair} + * uses existence of GroupedTaskView as control flow of how to animate in the incoming task. If + * we're launching from overview (from overview thumbnails) then pass in {@code true}, + * otherwise pass in {@code false} for case like quickswitching from home to task + */ + private void launchTaskInternal(@NonNull Consumer<Boolean> callback, boolean isQuickswitch, + boolean launchingExistingTaskView) { + getRecentsView().getSplitSelectController().launchExistingSplitPair( + launchingExistingTaskView ? this : null, mTask.key.id, mSecondaryTask.key.id, SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, callback, isQuickswitch, getSplitRatio()); } diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java index 4dbf4e3655..ebe4c663e3 100644 --- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -30,18 +30,20 @@ import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.util.AttributeSet; +import android.util.Log; import android.view.MotionEvent; -import android.view.Surface; import androidx.annotation.Nullable; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.LauncherState; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statehandlers.DesktopVisibilityController; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.statemanager.StateManager.StateListener; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.PendingSplitSelectInfo; import com.android.launcher3.util.SplitConfigurationOptions; @@ -81,14 +83,23 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher } @Override - public void startHome(boolean animated) { + protected void handleStartHome(boolean animated) { StateManager stateManager = mActivity.getStateManager(); animated &= stateManager.shouldAnimateStateChange(); stateManager.goToState(NORMAL, animated); + if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + mSplitSelectStateController.getSplitAnimationController() + .playPlaceholderDismissAnim(mActivity); + } AbstractFloatingView.closeAllOpenViews(mActivity, animated); } @Override + protected boolean canStartHomeSafely() { + return mActivity.canStartHomeSafely(); + } + + @Override protected void onTaskLaunchAnimationEnd(boolean success) { if (success) { mActivity.getStateManager().moveToRestState(); @@ -118,7 +129,9 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher @Override public void reset() { super.reset(); - setLayoutRotation(Surface.ROTATION_0, Surface.ROTATION_0); + + int recentsActivityRotation = getPagedViewOrientedState().getRecentsActivityRotation(); + setLayoutRotation(recentsActivityRotation, recentsActivityRotation); } @Override @@ -155,6 +168,8 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher @Override public void setOverviewStateEnabled(boolean enabled) { super.setOverviewStateEnabled(enabled); + Log.d(TestProtocol.OVERVIEW_OVER_HOME, "overview state enabled state has changed: " + + enabled); if (enabled) { LauncherState state = mActivity.getStateManager().getState(); boolean hasClearAllButton = (state.getVisibleElements(mActivity) @@ -218,7 +233,11 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher @Override protected boolean canLaunchFullscreenTask() { - return !mActivity.isInState(OVERVIEW_SPLIT_SELECT); + if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + return !mSplitSelectStateController.isSplitSelectActive(); + } else { + return !mActivity.isInState(OVERVIEW_SPLIT_SELECT); + } } @Override @@ -228,7 +247,7 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher DesktopVisibilityController desktopVisibilityController = mActivity.getDesktopVisibilityController(); if (desktopVisibilityController != null) { - desktopVisibilityController.setGestureInProgress(true); + desktopVisibilityController.setRecentsGestureStart(); } } @@ -236,9 +255,11 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher public void onGestureAnimationEnd() { DesktopVisibilityController desktopVisibilityController = null; boolean showDesktopApps = false; + GestureState.GestureEndTarget endTarget = null; if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) { desktopVisibilityController = mActivity.getDesktopVisibilityController(); - if (mCurrentGestureEndTarget == GestureState.GestureEndTarget.LAST_TASK + endTarget = mCurrentGestureEndTarget; + if (endTarget == GestureState.GestureEndTarget.LAST_TASK && desktopVisibilityController.areFreeformTasksVisible()) { // Recents gesture was cancelled and we are returning to the previous task. // After super class has handled clean up, show desktop apps on top again @@ -247,7 +268,7 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher } super.onGestureAnimationEnd(); if (desktopVisibilityController != null) { - desktopVisibilityController.setGestureInProgress(false); + desktopVisibilityController.setRecentsGestureEnd(endTarget); } if (showDesktopApps) { SystemUiProxy.INSTANCE.get(mActivity).showDesktopApps(mActivity.getDisplayId()); diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java index e47c0893e5..b31791a9a0 100644 --- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java +++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java @@ -258,6 +258,13 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo } /** + * Returns the visibility of the overview actions buttons. + */ + public @Visibility int getActionsButtonVisibility() { + return findViewById(R.id.action_buttons).getVisibility(); + } + + /** * Offsets OverviewActionsView horizontal position based on 3 button nav container in taskbar. */ private void updatePadding() { diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 55b9f95c5b..76f9c2c9c9 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -21,6 +21,16 @@ import static android.view.Surface.ROTATION_0; import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.makeMeasureSpec; +import static com.android.app.animation.Interpolators.ACCELERATE; +import static com.android.app.animation.Interpolators.ACCELERATE_0_75; +import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE; +import static com.android.app.animation.Interpolators.DECELERATE_2; +import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE; +import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.app.animation.Interpolators.FINAL_FRAME; +import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.OVERSHOOT_0_75; +import static com.android.app.animation.Interpolators.clampToProgress; import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU; import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; @@ -32,18 +42,7 @@ import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.Utilities.mapToRange; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.Utilities.squaredTouchSlop; -import static com.android.launcher3.anim.Interpolators.ACCEL; -import static com.android.launcher3.anim.Interpolators.ACCEL_0_75; -import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; -import static com.android.launcher3.anim.Interpolators.DEACCEL_2; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; -import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.OVERSHOOT_0_75; -import static com.android.launcher3.anim.Interpolators.clampToProgress; import static com.android.launcher3.config.FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW; -import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCH_FROM_STAGED_APP; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_ACTIONS_SPLIT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_CLEAR_ALL; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP; @@ -54,6 +53,7 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE; import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK; import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId; +import static com.android.quickstep.util.LogUtils.splitFailureMessage; import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA; import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED; import static com.android.quickstep.views.OverviewActionsView.FLAG_IS_NOT_TABLET; @@ -165,6 +165,7 @@ import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.util.ViewPool; import com.android.quickstep.BaseActivityInterface; import com.android.quickstep.GestureState; +import com.android.quickstep.OverviewCommandHelper; import com.android.quickstep.RecentsAnimationController; import com.android.quickstep.RecentsAnimationTargets; import com.android.quickstep.RecentsFilterState; @@ -173,6 +174,7 @@ import com.android.quickstep.RemoteAnimationTargets; import com.android.quickstep.RemoteTargetGluer; import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle; import com.android.quickstep.RotationTouchHelper; +import com.android.quickstep.SplitSelectionListener; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskOverlayFactory; import com.android.quickstep.TaskThumbnailCache; @@ -211,6 +213,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -661,7 +664,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T /** * Placeholder view indicating where the first split screen selected app will be placed */ - private SplitSelectStateController mSplitSelectStateController; + protected SplitSelectStateController mSplitSelectStateController; + /** * The first task that split screen selection was initiated with. When split select state is * initialized, we create a @@ -679,11 +683,22 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T private final Toast mSplitUnsupportedToast = Toast.makeText(getContext(), R.string.toast_split_app_unsupported, Toast.LENGTH_SHORT); - private SplitInstructionsView mSplitInstructionsView; - @Nullable private SplitSelectSource mSplitSelectSource; + private final SplitSelectionListener mSplitSelectionListener = new SplitSelectionListener() { + @Override + public void onSplitSelectionConfirmed() { } + + @Override + public void onSplitSelectionActive() { } + + @Override + public void onSplitSelectionExit(boolean launchedSplit) { + resetFromSplitSelectionState(); + } + }; + /** * Keeps track of the index of the TaskView that split screen was initialized with so we know * where to insert it back into list of taskViews in case user backs out of entering split @@ -694,8 +709,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T */ private int mSplitHiddenTaskViewIndex = -1; @Nullable - private FloatingTaskView mFirstFloatingTaskView; - @Nullable private FloatingTaskView mSecondFloatingTaskView; /** @@ -992,16 +1005,34 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } /** - * Update the thumbnail of the task. + * Update the thumbnail(s) of the relevant TaskView. * @param refreshNow Refresh immediately if it's true. */ @Nullable - public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData, boolean refreshNow) { - TaskView taskView = getTaskViewByTaskId(taskId); - if (taskView != null) { - taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData, refreshNow); + public TaskView updateThumbnail( + HashMap<Integer, ThumbnailData> thumbnailData, boolean refreshNow) { + TaskView updatedTaskView = null; + for (Map.Entry<Integer, ThumbnailData> entry : thumbnailData.entrySet()) { + Integer id = entry.getKey(); + ThumbnailData thumbnail = entry.getValue(); + TaskView taskView = getTaskViewByTaskId(id); + if (taskView == null) { + continue; + } + // taskView could be a GroupedTaskView, so select the relevant task by ID + TaskIdAttributeContainer taskAttributes = taskView.getTaskAttributesById(id); + if (taskAttributes == null) { + continue; + } + Task task = taskAttributes.getTask(); + TaskThumbnailView taskThumbnailView = taskAttributes.getThumbnailView(); + taskThumbnailView.setThumbnail(task, thumbnail, refreshNow); + // thumbnailData can contain 1-2 ids, but they should correspond to the same + // TaskView, so overwriting is ok + updatedTaskView = taskView; } - return taskView; + + return updatedTaskView; } @Override @@ -1048,6 +1079,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T mIPipAnimationListener); mOrientationState.initListeners(); mTaskOverlayFactory.initListeners(); + if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + mSplitSelectStateController.registerSplitListener(mSplitSelectionListener); + } } @Override @@ -1066,6 +1100,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T mIPipAnimationListener.setActivityAndRecentsView(null, null); mOrientationState.destroyListeners(); mTaskOverlayFactory.removeListeners(); + if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + mSplitSelectStateController.unregisterSplitListener(mSplitSelectionListener); + } } @Override @@ -1180,7 +1217,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T new SurfaceTransactionApplier(mActivity.getDragLayer()); ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1); appAnimator.setDuration(RECENTS_LAUNCH_DURATION); - appAnimator.setInterpolator(ACCEL_DEACCEL); + appAnimator.setInterpolator(ACCELERATE_DECELERATE); appAnimator.addUpdateListener(valueAnimator -> { float percent = valueAnimator.getAnimatedFraction(); SurfaceTransaction transaction = new SurfaceTransaction(); @@ -1330,9 +1367,15 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T return null; } + // We're looking for a taskView that matches these ids, regardless of order + int[] taskIdsCopy = Arrays.copyOf(taskIds, taskIds.length); + Arrays.sort(taskIdsCopy); + for (int i = 0; i < getTaskViewCount(); i++) { TaskView taskView = requireTaskViewAt(i); - if (Arrays.equals(taskIds, taskView.getTaskIds())) { + int[] taskViewIdsCopy = taskView.getTaskIds(); + Arrays.sort(taskViewIdsCopy); + if (Arrays.equals(taskIdsCopy, taskViewIdsCopy)) { return taskView; } } @@ -1353,6 +1396,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T // its thumbnail mTmpRunningTasks = null; mSplitBoundsConfig = null; + mTaskOverlayFactory.clearAllActiveState(); } updateLocusId(); } @@ -1631,6 +1675,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T int[] runningTaskId = getTaskIdsForTaskViewId(mRunningTaskViewId); int[] focusedTaskId = getTaskIdsForTaskViewId(mFocusedTaskViewId); + // Reset the focused task to avoiding initializing TaskViews layout as focused task during + // binding. The focused task view will be updated after all the TaskViews are bound. + mFocusedTaskViewId = INVALID_TASK_ID; + // Removing views sets the currentPage to 0, so we save this and restore it after // the new set of views are added int previousCurrentPage = mCurrentPage; @@ -1639,8 +1687,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T // If we are entering Overview as a result of initiating a split from somewhere else // (e.g. split from Home), we need to make sure the staged app is not drawn as a thumbnail. int stagedTaskIdToBeRemovedFromGrid; - if (mSplitSelectSource != null) { - stagedTaskIdToBeRemovedFromGrid = mSplitSelectSource.alreadyRunningTaskId; + if (isSplitSelectionActive()) { + stagedTaskIdToBeRemovedFromGrid = mSplitSelectStateController.getInitialTaskId(); updateCurrentTaskActionsVisibility(); } else { stagedTaskIdToBeRemovedFromGrid = INVALID_TASK_ID; @@ -1737,7 +1785,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T mFocusedTaskViewId = newFocusedTaskView != null && !ENABLE_GRID_ONLY_OVERVIEW.get() ? newFocusedTaskView.getTaskViewId() : INVALID_TASK_ID; updateTaskSize(); - updateChildTaskOrientations(); + if (newFocusedTaskView != null) { + newFocusedTaskView.setOrientationState(mOrientationState); + } TaskView newRunningTaskView = null; if (hasAnyValidTaskIds(runningTaskId)) { @@ -2322,7 +2372,15 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T startHome(mActivity.isStarted()); } - public abstract void startHome(boolean animated); + public void startHome(boolean animated) { + if (!canStartHomeSafely()) return; + handleStartHome(animated); + } + + protected abstract void handleStartHome(boolean animated); + + /** Returns whether user can start home based on state in {@link OverviewCommandHelper}. */ + protected abstract boolean canStartHomeSafely(); public void reset() { setCurrentTask(-1); @@ -2344,7 +2402,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T remoteTargetHandle.getTransformParams().setTargetSet(null); remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false); }); - resetFromSplitSelectionState(); + if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + resetFromSplitSelectionState(); + } // These are relatively expensive and don't need to be done this frame (RecentsView isn't // visible anyway), so defer by a frame to get off the critical path, e.g. app to home. @@ -3126,7 +3186,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T // Use setFloat instead of setViewAlpha as we want to keep the view visible even when it's // alpha is set to 0 so that it can be recycled in the view pool properly anim.setFloat(taskView, VIEW_ALPHA, 0, - clampToProgress(isOnGridBottomRow(taskView) ? ACCEL : FINAL_FRAME, 0, 0.5f)); + clampToProgress(isOnGridBottomRow(taskView) ? ACCELERATE : FINAL_FRAME, 0, 0.5f)); FloatProperty<TaskView> secondaryViewTranslate = taskView.getSecondaryDismissTranslationProperty(); int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView); @@ -3165,7 +3225,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T AnimUtils.getDeviceOverviewToSplitTimings(mActivity.getDeviceProfile().isTablet); RectF startingTaskRect = new RectF(); - safeRemoveDragLayerView(mFirstFloatingTaskView); + safeRemoveDragLayerView(mSplitSelectStateController.getFirstFloatingTaskView()); SplitAnimInitProps splitAnimInitProps = mSplitSelectStateController.getSplitAnimationController().getFirstAnimInitViews( () -> mSplitHiddenTaskView, () -> mSplitSelectSource); @@ -3178,33 +3238,34 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T timings.getIconFadeEndOffset())); } - mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity, + FloatingTaskView firstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity, splitAnimInitProps.getOriginalView(), splitAnimInitProps.getOriginalBitmap(), splitAnimInitProps.getIconDrawable(), startingTaskRect); - mFirstFloatingTaskView.setAlpha(1); - mFirstFloatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect, + firstFloatingTaskView.setAlpha(1); + firstFloatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect, splitAnimInitProps.getFadeWithThumbnail(), splitAnimInitProps.isStagedTask()); + mSplitSelectStateController.setFirstFloatingTaskView(firstFloatingTaskView); // Allow user to click staged app to launch into fullscreen - if (ENABLE_LAUNCH_FROM_STAGED_APP.get()) { - mFirstFloatingTaskView.setOnClickListener(this::animateToFullscreen); - } + firstFloatingTaskView.setOnClickListener(this::animateToFullscreen); // SplitInstructionsView: animate in - safeRemoveDragLayerView(mSplitInstructionsView); - mSplitInstructionsView = SplitInstructionsView.getSplitInstructionsView(mActivity); - mSplitInstructionsView.setAlpha(0); - anim.setViewAlpha(mSplitInstructionsView, 1, clampToProgress(LINEAR, + safeRemoveDragLayerView(mSplitSelectStateController.getSplitInstructionsView()); + SplitInstructionsView splitInstructionsView = + SplitInstructionsView.getSplitInstructionsView(mActivity); + splitInstructionsView.setAlpha(0); + anim.setViewAlpha(splitInstructionsView, 1, clampToProgress(LINEAR, timings.getInstructionsContainerFadeInStartOffset(), timings.getInstructionsContainerFadeInEndOffset())); - anim.setViewAlpha(mSplitInstructionsView.getTextView(), 1, clampToProgress(LINEAR, + anim.setViewAlpha(splitInstructionsView.getTextView(), 1, clampToProgress(LINEAR, timings.getInstructionsTextFadeInStartOffset(), timings.getInstructionsTextFadeInEndOffset())); - anim.addFloat(mSplitInstructionsView, mSplitInstructionsView.UNFOLD, 0.1f, 1, + anim.addFloat(splitInstructionsView, splitInstructionsView.UNFOLD, 0.1f, 1, clampToProgress(EMPHASIZED_DECELERATE, timings.getInstructionsUnfoldStartOffset(), timings.getInstructionsUnfoldEndOffset())); + mSplitSelectStateController.setSplitInstructionsView(splitInstructionsView); InteractionJankMonitorWrapper.begin(this, InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "First tile selected"); @@ -3227,7 +3288,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER); } else { // If transition to split select was interrupted, clean up to prevent glitches - resetFromSplitSelectionState(); + if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + mSplitSelectStateController.resetState(); + } else { + resetFromSplitSelectionState(); + } InteractionJankMonitorWrapper.cancel( InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER); } @@ -3260,8 +3325,13 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T true /* isStagedTask */); pendingAnimation.addEndListener(animationSuccess -> - mSplitSelectStateController.launchInitialAppFullscreen(launchSuccess -> - resetFromSplitSelectionState())); + mSplitSelectStateController.launchInitialAppFullscreen(launchSuccess -> { + if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + mSplitSelectStateController.resetState(); + } else { + resetFromSplitSelectionState(); + } + })); pendingAnimation.buildAnim().start(); } @@ -3968,13 +4038,12 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T private void removeTaskInternal(int dismissedTaskViewId) { int[] taskIds = getTaskIdsForTaskViewId(dismissedTaskViewId); - int primaryTaskId = taskIds[0]; - int secondaryTaskId = taskIds[1]; UI_HELPER_EXECUTOR.getHandler().post( () -> { - ActivityManagerWrapper.getInstance().removeTask(primaryTaskId); - if (secondaryTaskId != -1) { - ActivityManagerWrapper.getInstance().removeTask(secondaryTaskId); + for (int taskId : taskIds) { + if (taskId != -1) { + ActivityManagerWrapper.getInstance().removeTask(taskId); + } } }); } @@ -4294,7 +4363,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T private void updatePageOffsets() { float offset = mAdjacentPageHorizontalOffset; - float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness); + float modalOffset = ACCELERATE_0_75.getInterpolation(mTaskModalness); int count = getChildCount(); boolean showAsGrid = showAsGrid(); @@ -4662,6 +4731,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T return false; } if (mSplitSelectStateController.isBothSplitAppsConfirmed()) { + Log.w(TAG, splitFailureMessage( + "confirmSplitSelect", "both apps have already been set")); return true; } // Second task is selected either as an already-running Task or an Intent @@ -4669,6 +4740,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T if (!task.isDockable) { // Task does not support split screen mSplitUnsupportedToast.show(); + Log.w(TAG, splitFailureMessage("confirmSplitSelect", + "selected Task (" + task.key.getPackageName() + + ") is not dockable / does not support splitscreen")); return true; } mSplitSelectStateController.setSecondTask(task); @@ -4693,8 +4767,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T mSplitSelectStateController.getActiveSplitStagePosition(), firstTaskEndingBounds, secondTaskEndingBounds); - mFirstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds); - mFirstFloatingTaskView.addConfirmAnimation(pendingAnimation, + FloatingTaskView firstFloatingTaskView = + mSplitSelectStateController.getFirstFloatingTaskView(); + firstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds); + firstFloatingTaskView.addConfirmAnimation(pendingAnimation, new RectF(firstTaskStartingBounds), firstTaskEndingBounds, false /* fadeWithThumbnail */, true /* isStagedTask */); @@ -4706,14 +4782,21 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T mSecondFloatingTaskView.addConfirmAnimation(pendingAnimation, secondTaskStartingBounds, secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */); - pendingAnimation.setViewAlpha(mSplitInstructionsView, 0, clampToProgress(LINEAR, - timings.getInstructionsFadeStartOffset(), - timings.getInstructionsFadeEndOffset())); + pendingAnimation.setViewAlpha(mSplitSelectStateController.getSplitInstructionsView(), 0, + clampToProgress(LINEAR, timings.getInstructionsFadeStartOffset(), + timings.getInstructionsFadeEndOffset())); pendingAnimation.addEndListener(aBoolean -> { mSplitSelectStateController.launchSplitTasks( - aBoolean1 -> RecentsView.this.resetFromSplitSelectionState()); - InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER); + aBoolean1 -> { + if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + mSplitSelectStateController.resetState(); + } else { + resetFromSplitSelectionState(); + } + InteractionJankMonitorWrapper.end( + InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER); + }); }); mSecondSplitHiddenView = containerTaskView; @@ -4727,21 +4810,22 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T // Fade out all other views underneath placeholders ObjectAnimator tvFade = ObjectAnimator.ofFloat(this, RecentsView.CONTENT_ALPHA,1, 0); - pendingAnimation.add(tvFade, DEACCEL_2, SpringProperty.DEFAULT); + pendingAnimation.add(tvFade, DECELERATE_2, SpringProperty.DEFAULT); pendingAnimation.buildAnim().start(); return true; } @SuppressLint("WrongCall") protected void resetFromSplitSelectionState() { - if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1) { - safeRemoveDragLayerView(mFirstFloatingTaskView); + if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1 || + FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + safeRemoveDragLayerView(mSplitSelectStateController.getFirstFloatingTaskView()); safeRemoveDragLayerView(mSecondFloatingTaskView); - safeRemoveDragLayerView(mSplitInstructionsView); - mFirstFloatingTaskView = null; + safeRemoveDragLayerView(mSplitSelectStateController.getSplitInstructionsView()); mSecondFloatingTaskView = null; - mSplitInstructionsView = null; mSplitSelectSource = null; + mSplitSelectStateController.getSplitAnimationController() + .removeSplitInstructionsView(mActivity); } if (mSecondSplitHiddenView != null) { @@ -4754,7 +4838,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T setTaskViewsPrimarySplitTranslation(0); setTaskViewsSecondarySplitTranslation(0); - mSplitSelectStateController.resetState(); + if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + // When flag is on, this method gets called from resetState() call below, let's avoid + // infinite recursion today + mSplitSelectStateController.resetState(); + } if (mSplitHiddenTaskViewIndex == -1) { return; } @@ -4821,8 +4909,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T mSplitPlaceholderInset, mActivity.getDeviceProfile(), mSplitSelectStateController.getActiveSplitStagePosition(), mTempRect); mTempRectF.set(mTempRect); - mFirstFloatingTaskView.updateOrientationHandler(mOrientationHandler); - mFirstFloatingTaskView.update(mTempRectF, /*progress=*/1f); + FloatingTaskView firstFloatingTaskView = + mSplitSelectStateController.getFirstFloatingTaskView(); + firstFloatingTaskView.updateOrientationHandler(mOrientationHandler); + firstFloatingTaskView.update(mTempRectF, /*progress=*/1f); PagedOrientationHandler orientationHandler = getPagedOrientationHandler(); Pair<FloatProperty, FloatProperty> taskViewsFloat = @@ -4832,8 +4922,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T taskViewsFloat.first.set(this, getSplitSelectTranslation()); taskViewsFloat.second.set(this, 0f); - if (mSplitInstructionsView != null) { - mSplitInstructionsView.ensureProperRotation(); + if (mSplitSelectStateController.getSplitInstructionsView() != null) { + mSplitSelectStateController.getSplitInstructionsView().ensureProperRotation(); } } @@ -5152,7 +5242,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T true /* forDesktop */); mRemoteTargetHandles = gluer.assignTargetsForDesktop(recentsAnimationTargets); } else { - gluer = new RemoteTargetGluer(getContext(), getSizeStrategy()); + gluer = new RemoteTargetGluer(getContext(), getSizeStrategy(), recentsAnimationTargets, + false); mRemoteTargetHandles = gluer.assignTargetsForSplitScreen(recentsAnimationTargets); } mSplitBoundsConfig = gluer.getSplitBounds(); @@ -5207,6 +5298,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T cleanupRemoteTargets(); if (mRecentsAnimationController == null) { + Log.d(TestProtocol.INCORRECT_HOME_STATE, "finish recents animation but recents " + + "animation controller was null. returning."); if (onFinishComplete != null) { onFinishComplete.run(); } @@ -5943,13 +6036,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } @Nullable - public FloatingTaskView getFirstFloatingTaskView() { - return mFirstFloatingTaskView; - } - - @Nullable public SplitInstructionsView getSplitInstructionsView() { - return mSplitInstructionsView; + return mSplitSelectStateController.getSplitInstructionsView(); } /** Update the current activity locus id to show the enabled state of Overview */ diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java index 0d9e41243b..4ca02e0a13 100644 --- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java +++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java @@ -17,15 +17,21 @@ package com.android.quickstep.views; import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.util.AttributeSet; import android.util.FloatProperty; +import android.view.MotionEvent; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import androidx.annotation.Nullable; import androidx.appcompat.widget.AppCompatTextView; +import com.android.launcher3.LauncherState; import com.android.launcher3.R; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.statemanager.StatefulActivity; /** @@ -65,7 +71,7 @@ public class SplitInstructionsView extends FrameLayout { mLauncher = (StatefulActivity) context; } - static SplitInstructionsView getSplitInstructionsView(StatefulActivity launcher) { + public static SplitInstructionsView getSplitInstructionsView(StatefulActivity launcher) { ViewGroup dragLayer = launcher.getDragLayer(); final SplitInstructionsView splitInstructionsView = (SplitInstructionsView) launcher.getLayoutInflater().inflate( @@ -73,9 +79,7 @@ public class SplitInstructionsView extends FrameLayout { dragLayer, false ); - - splitInstructionsView.mTextView = splitInstructionsView.findViewById( - R.id.split_instructions_text); + splitInstructionsView.init(); // Since textview overlays base view, and we sometimes manipulate the alpha of each // simultaneously, force overlapping rendering to false prevents redrawing of pixels, @@ -92,6 +96,71 @@ public class SplitInstructionsView extends FrameLayout { ensureProperRotation(); } + private void init() { + mTextView = findViewById(R.id.split_instructions_text); + + if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + mTextView.setCompoundDrawables(null, null, null, null); + return; + } + + mTextView.setOnTouchListener((v, event) -> { + if (isTouchInsideRightCompoundDrawable(event)) { + if (event.getAction() == MotionEvent.ACTION_UP) { + exitSplitSelection(); + } + return true; + } + return false; + }); + } + + private void exitSplitSelection() { + ((RecentsView) mLauncher.getOverviewPanel()).getSplitSelectController() + .getSplitAnimationController().playPlaceholderDismissAnim(mLauncher); + mLauncher.getStateManager().goToState(LauncherState.NORMAL); + } + + private boolean isTouchInsideRightCompoundDrawable(MotionEvent event) { + // Get the right compound drawable of the TextView. + Drawable rightDrawable = mTextView.getCompoundDrawablesRelative()[2]; + + // Check if the touch event intersects with the drawable's bounds. + if (rightDrawable != null) { + // We can get away w/o caring about the Y bounds since it's such a small view, if it's + // above/below the drawable just assume they meant to touch it. ¯\_(ツ)_/¯ + return event.getX() >= (mTextView.getWidth() - rightDrawable.getBounds().width()); + } else { + return false; + } + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + return; + } + + info.addAction(new AccessibilityNodeInfo.AccessibilityAction( + R.string.toast_split_select_cont_desc, + getResources().getString(R.string.toast_split_select_cont_desc) + )); + } + + @Override + public boolean performAccessibilityAction(int action, Bundle arguments) { + if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + return super.performAccessibilityAction(action, arguments); + } + + if (action == R.string.toast_split_select_cont_desc) { + exitSplitSelection(); + return true; + } + return super.performAccessibilityAction(action, arguments); + } + void ensureProperRotation() { ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler() .setSplitInstructionsParams( diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java index 2c9afb4b4c..b4d24e5b59 100644 --- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java +++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java @@ -36,12 +36,12 @@ import android.widget.TextView; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.anim.AnimationSuccessListener; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.touch.PagedOrientationHandler; @@ -256,7 +256,7 @@ public class TaskMenuView extends AbstractFloatingView { final Animator revealAnimator = createOpenCloseOutlineProvider() .createRevealAnimator(this, closing); - revealAnimator.setInterpolator(Interpolators.DEACCEL); + revealAnimator.setInterpolator(Interpolators.DECELERATE); mOpenCloseAnimator.playTogether(revealAnimator, ObjectAnimator.ofFloat( mTaskContainer.getThumbnailView(), DIM_ALPHA, diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 83a5c72127..39b6c62949 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -20,11 +20,11 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.view.Display.DEFAULT_DISPLAY; import static android.widget.Toast.LENGTH_SHORT; +import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE; +import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor; -import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; @@ -32,6 +32,7 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT; import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED; import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition; +import static com.android.quickstep.TaskOverlayFactory.getEnabledShortcuts; import static com.android.quickstep.util.BorderAnimator.DEFAULT_BORDER_COLOR; import static java.lang.annotation.RetentionPolicy.SOURCE; @@ -70,12 +71,13 @@ import android.widget.Toast; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import com.android.app.animation.Interpolators; import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; @@ -88,6 +90,7 @@ import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; +import com.android.launcher3.util.TraceHelper; import com.android.launcher3.util.TransformingTouchDelegate; import com.android.launcher3.util.ViewPool.Reusable; import com.android.quickstep.RecentsModel; @@ -95,7 +98,6 @@ import com.android.quickstep.RemoteAnimationTargets; import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle; import com.android.quickstep.TaskAnimationManager; import com.android.quickstep.TaskIconCache; -import com.android.quickstep.TaskOverlayFactory; import com.android.quickstep.TaskThumbnailCache; import com.android.quickstep.TaskUtils; import com.android.quickstep.TaskViewUtils; @@ -132,15 +134,17 @@ public class TaskView extends FrameLayout implements Reusable { public static final int FLAG_UPDATE_ICON = 1; public static final int FLAG_UPDATE_THUMBNAIL = FLAG_UPDATE_ICON << 1; + public static final int FLAG_UPDATE_CORNER_RADIUS = FLAG_UPDATE_THUMBNAIL << 1; - public static final int FLAG_UPDATE_ALL = FLAG_UPDATE_ICON | FLAG_UPDATE_THUMBNAIL; + public static final int FLAG_UPDATE_ALL = FLAG_UPDATE_ICON | FLAG_UPDATE_THUMBNAIL + | FLAG_UPDATE_CORNER_RADIUS; /** * Used in conjunction with {@link #onTaskListVisibilityChanged(boolean, int)}, providing more * granularity on which components of this task require an update */ @Retention(SOURCE) - @IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL}) + @IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL, FLAG_UPDATE_CORNER_RADIUS}) public @interface TaskDataChanges {} /** @@ -163,7 +167,7 @@ public class TaskView extends FrameLayout implements Reusable { public static final long SCALE_ICON_DURATION = 120; private static final long DIM_ANIM_DURATION = 700; - private static final Interpolator GRID_INTERPOLATOR = ACCEL_DEACCEL; + private static final Interpolator GRID_INTERPOLATOR = ACCELERATE_DECELERATE; /** * This technically can be a vanilla {@link TouchDelegate} class, however that class requires @@ -411,7 +415,9 @@ public class TaskView extends FrameLayout implements Reusable { private boolean mIsClickableAsLiveTile = true; - @Nullable private final BorderAnimator mBorderAnimator; + @Nullable private final BorderAnimator mFocusBorderAnimator; + + @Nullable private final BorderAnimator mHoverBorderAnimator; public TaskView(Context context) { this(context, null); @@ -437,23 +443,40 @@ public class TaskView extends FrameLayout implements Reusable { boolean keyboardFocusHighlightEnabled = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get() || DesktopTaskView.DESKTOP_MODE_SUPPORTED; - setWillNotDraw(!keyboardFocusHighlightEnabled); - - TypedArray ta = context.obtainStyledAttributes( - attrs, R.styleable.TaskView, defStyleAttr, defStyleRes); - - mBorderAnimator = !keyboardFocusHighlightEnabled - ? null - : new BorderAnimator( - /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius, - /* borderColor= */ ta.getColor( - R.styleable.TaskView_borderColor, DEFAULT_BORDER_COLOR), - /* borderAnimationParams= */ new BorderAnimator.SimpleParams( - /* borderWidthPx= */ context.getResources().getDimensionPixelSize( - R.dimen.keyboard_quick_switch_border_width), - /* boundsBuilder= */ this::updateBorderBounds, - /* targetView= */ this)); - ta.recycle(); + boolean willDrawBorder = + keyboardFocusHighlightEnabled || FeatureFlags.ENABLE_CURSOR_HOVER_STATES.get(); + setWillNotDraw(!willDrawBorder); + + if (willDrawBorder) { + TypedArray styledAttrs = context.obtainStyledAttributes( + attrs, R.styleable.TaskView, defStyleAttr, defStyleRes); + + mFocusBorderAnimator = keyboardFocusHighlightEnabled ? new BorderAnimator( + /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius, + /* borderColor= */ styledAttrs.getColor( + R.styleable.TaskView_focusBorderColor, DEFAULT_BORDER_COLOR), + /* borderAnimationParams= */ new BorderAnimator.SimpleParams( + /* borderWidthPx= */ context.getResources().getDimensionPixelSize( + R.dimen.keyboard_quick_switch_border_width), + /* boundsBuilder= */ this::updateBorderBounds, + /* targetView= */ this)) : null; + + mHoverBorderAnimator = + FeatureFlags.ENABLE_CURSOR_HOVER_STATES.get() ? new BorderAnimator( + /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius, + /* borderColor= */ styledAttrs.getColor( + R.styleable.TaskView_hoverBorderColor, DEFAULT_BORDER_COLOR), + /* borderAnimationParams= */ new BorderAnimator.SimpleParams( + /* borderWidthPx= */ context.getResources() + .getDimensionPixelSize(R.dimen.task_hover_border_width), + /* boundsBuilder= */ this::updateBorderBounds, + /* targetView= */ this)) : null; + + styledAttrs.recycle(); + } else { + mFocusBorderAnimator = null; + mHoverBorderAnimator = null; + } } protected void updateBorderBounds(Rect bounds) { @@ -507,16 +530,48 @@ public class TaskView extends FrameLayout implements Reusable { @Override protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); - if (mBorderAnimator != null) { - mBorderAnimator.buildAnimator(gainFocus).start(); + if (mFocusBorderAnimator != null) { + mFocusBorderAnimator.buildAnimator(gainFocus).start(); + } + } + + @Override + public boolean onHoverEvent(MotionEvent event) { + if (FeatureFlags.ENABLE_CURSOR_HOVER_STATES.get()) { + switch (event.getAction()) { + case MotionEvent.ACTION_HOVER_ENTER: + mHoverBorderAnimator.buildAnimator(/* isAppearing= */ true).start(); + break; + case MotionEvent.ACTION_HOVER_EXIT: + mHoverBorderAnimator.buildAnimator(/* isAppearing= */ false).start(); + break; + default: + break; + } + } + return super.onHoverEvent(event); + } + + @Override + public boolean onInterceptHoverEvent(MotionEvent event) { + if (FeatureFlags.ENABLE_CURSOR_HOVER_STATES.get()) { + // avoid triggering hover event on child elements which would cause HOVER_EXIT for this + // task view + return true; + } else { + return super.onInterceptHoverEvent(event); } } @Override public void draw(Canvas canvas) { super.draw(canvas); - if (mBorderAnimator != null) { - mBorderAnimator.drawBorder(canvas); + if (mFocusBorderAnimator != null) { + mFocusBorderAnimator.drawBorder(canvas); + } + + if (mHoverBorderAnimator != null) { + mHoverBorderAnimator.drawBorder(canvas); } } @@ -580,7 +635,6 @@ public class TaskView extends FrameLayout implements Reusable { mIconView, STAGE_POSITION_UNDEFINED); mSnapshotView.bind(task); setOrientationState(orientedState); - mDigitalWellBeingToast.initialize(mTask); } /** @@ -958,6 +1012,9 @@ public class TaskView extends FrameLayout implements Reusable { anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { + if (!recentsView.showAsGrid()) { + return; + } recentsView.runActionOnRemoteHandles( (Consumer<RemoteTargetHandle>) remoteTargetHandle -> remoteTargetHandle @@ -1028,6 +1085,9 @@ public class TaskView extends FrameLayout implements Reusable { mDigitalWellBeingToast.initialize(task); }); } + if (needsUpdate(changes, FLAG_UPDATE_CORNER_RADIUS)) { + mCurrentFullscreenParams.updateCornerRadius(getContext()); + } } else { if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) { mSnapshotView.setThumbnail(null, null); @@ -1563,8 +1623,8 @@ public class TaskView extends FrameLayout implements Reusable { if (taskContainer == null) { continue; } - for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this, - taskContainer)) { + for (SystemShortcut s : TraceHelper.allowIpcs( + "TV.a11yInfo", () -> getEnabledShortcuts(this, taskContainer))) { info.addAction(s.createAccessibilityAction(context)); } } @@ -1601,7 +1661,7 @@ public class TaskView extends FrameLayout implements Reusable { if (taskContainer == null) { continue; } - for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this, + for (SystemShortcut s : getEnabledShortcuts(this, taskContainer)) { if (s.hasHandlerForAction(action)) { s.onClick(this); @@ -1656,10 +1716,15 @@ public class TaskView extends FrameLayout implements Reusable { } void updateCurrentFullscreenParams(PreviewPositionHelper previewPositionHelper) { + updateFullscreenParams(mCurrentFullscreenParams, previewPositionHelper); + } + + protected void updateFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams, + PreviewPositionHelper previewPositionHelper) { if (getRecentsView() == null) { return; } - mCurrentFullscreenParams.setProgress(mFullscreenProgress, getRecentsView().getScaleX(), + fullscreenParams.setProgress(mFullscreenProgress, getRecentsView().getScaleX(), getScaleX(), getWidth(), mActivity.getDeviceProfile(), previewPositionHelper); } @@ -1803,16 +1868,29 @@ public class TaskView extends FrameLayout implements Reusable { */ public static class FullscreenDrawParams { - private final float mCornerRadius; - private final float mWindowCornerRadius; + private float mCornerRadius; + private float mWindowCornerRadius; public float mCurrentDrawnCornerRadius; public FullscreenDrawParams(Context context) { - mCornerRadius = TaskCornerRadius.get(context); - mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context); + updateCornerRadius(context); + } + + /** Recomputes the start and end corner radius for the given Context. */ + public void updateCornerRadius(Context context) { + mCornerRadius = computeTaskCornerRadius(context); + mWindowCornerRadius = computeWindowCornerRadius(context); + } + + @VisibleForTesting + public float computeTaskCornerRadius(Context context) { + return TaskCornerRadius.get(context); + } - mCurrentDrawnCornerRadius = mCornerRadius; + @VisibleForTesting + public float computeWindowCornerRadius(Context context) { + return QuickStepContract.getWindowCornerRadius(context); } /** diff --git a/quickstep/tests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt b/quickstep/tests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt new file mode 100644 index 0000000000..a5327628d1 --- /dev/null +++ b/quickstep/tests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt @@ -0,0 +1,127 @@ +package com.android.launcher3.model + +import android.app.prediction.AppPredictor +import android.app.prediction.AppTarget +import android.app.prediction.AppTargetEvent +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.launcher3.LauncherAppState +import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION +import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION +import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WALLPAPERS +import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION +import com.android.launcher3.util.LauncherModelHelper +import org.junit.After +import org.junit.Assert.assertNotSame +import org.junit.Assert.assertSame +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyZeroInteractions +import org.mockito.MockitoAnnotations + +/** Unit tests for [QuickstepModelDelegate]. */ +@RunWith(AndroidJUnit4::class) +class QuickstepModelDelegateTest { + + private lateinit var underTest: QuickstepModelDelegate + private lateinit var modelHelper: LauncherModelHelper + + @Mock private lateinit var target: AppTarget + @Mock private lateinit var mockedAppTargetEvent: AppTargetEvent + @Mock private lateinit var allAppsPredictor: AppPredictor + @Mock private lateinit var hotseatPredictor: AppPredictor + @Mock private lateinit var widgetRecommendationPredictor: AppPredictor + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + modelHelper = LauncherModelHelper() + underTest = QuickstepModelDelegate(modelHelper.sandboxContext) + underTest.mAllAppsState.predictor = allAppsPredictor + underTest.mHotseatState.predictor = hotseatPredictor + underTest.mWidgetsRecommendationState.predictor = widgetRecommendationPredictor + underTest.mApp = LauncherAppState.getInstance(modelHelper.sandboxContext) + underTest.mDataModel = BgDataModel() + } + + @After + fun tearDown() { + modelHelper.destroy() + } + + @Test + fun onAppTargetEvent_notifyTarget() { + underTest.onAppTargetEvent(mockedAppTargetEvent, CONTAINER_PREDICTION) + + verify(allAppsPredictor).notifyAppTargetEvent(mockedAppTargetEvent) + verifyZeroInteractions(hotseatPredictor) + verifyZeroInteractions(widgetRecommendationPredictor) + } + + @Test + fun onWidgetPrediction_notifyWidgetRecommendationPredictor() { + underTest.onAppTargetEvent(mockedAppTargetEvent, CONTAINER_WIDGETS_PREDICTION) + + verifyZeroInteractions(allAppsPredictor) + verify(widgetRecommendationPredictor).notifyAppTargetEvent(mockedAppTargetEvent) + verifyZeroInteractions(hotseatPredictor) + } + + @Test + fun onHotseatPrediction_notifyHotseatPredictor() { + underTest.onAppTargetEvent(mockedAppTargetEvent, CONTAINER_HOTSEAT_PREDICTION) + + verifyZeroInteractions(allAppsPredictor) + verifyZeroInteractions(widgetRecommendationPredictor) + verify(hotseatPredictor).notifyAppTargetEvent(mockedAppTargetEvent) + } + + @Test + fun onOtherClient_notifyHotseatPredictor() { + underTest.onAppTargetEvent(mockedAppTargetEvent, CONTAINER_WALLPAPERS) + + verifyZeroInteractions(allAppsPredictor) + verifyZeroInteractions(widgetRecommendationPredictor) + verify(hotseatPredictor).notifyAppTargetEvent(mockedAppTargetEvent) + } + + @Test + fun hotseatActionPin_recreateHotSeat() { + assertSame(underTest.mHotseatState.predictor, hotseatPredictor) + val appTargetEvent = AppTargetEvent.Builder(target, AppTargetEvent.ACTION_PIN).build() + underTest.markActive() + + underTest.onAppTargetEvent(appTargetEvent, CONTAINER_HOTSEAT_PREDICTION) + + verify(hotseatPredictor).destroy() + assertNotSame(underTest.mHotseatState.predictor, hotseatPredictor) + } + + @Test + fun hotseatActionUnpin_recreateHotSeat() { + assertSame(underTest.mHotseatState.predictor, hotseatPredictor) + underTest.markActive() + val appTargetEvent = AppTargetEvent.Builder(target, AppTargetEvent.ACTION_UNPIN).build() + + underTest.onAppTargetEvent(appTargetEvent, CONTAINER_HOTSEAT_PREDICTION) + + verify(hotseatPredictor).destroy() + assertNotSame(underTest.mHotseatState.predictor, hotseatPredictor) + } + + @Test + fun container_actionPin_notRecreateHotSeat() { + assertSame(underTest.mHotseatState.predictor, hotseatPredictor) + val appTargetEvent = AppTargetEvent.Builder(target, AppTargetEvent.ACTION_UNPIN).build() + underTest.markActive() + + underTest.onAppTargetEvent(appTargetEvent, CONTAINER_PREDICTION) + + verify(allAppsPredictor, never()).destroy() + verify(hotseatPredictor, never()).destroy() + assertSame(underTest.mHotseatState.predictor, hotseatPredictor) + } +} diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java index 83341cb868..b12d98b8ce 100644 --- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java +++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java @@ -20,6 +20,7 @@ import static android.os.Process.myUserHandle; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static com.android.launcher3.util.TestUtil.runOnExecutorSync; import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo; import static com.google.common.truth.Truth.assertThat; @@ -40,11 +41,9 @@ import android.text.TextUtils; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.icons.ComponentWithLabel; -import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.QuickstepModelDelegate.PredictorState; +import com.android.launcher3.util.LauncherLayoutBuilder; import com.android.launcher3.util.LauncherModelHelper; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; @@ -53,8 +52,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import java.util.Arrays; import java.util.List; @@ -76,17 +73,9 @@ public final class WidgetsPredicationUpdateTaskTest { private LauncherModelHelper mModelHelper; private UserHandle mUserHandle; - @Mock - private IconCache mIconCache; - @Before public void setup() throws Exception { mModelHelper = new LauncherModelHelper(); - MockitoAnnotations.initMocks(this); - doAnswer(invocation -> { - ComponentWithLabel componentWithLabel = invocation.getArgument(0); - return componentWithLabel.getComponent().getShortClassName(); - }).when(mIconCache).getTitleNoCache(any()); mUserHandle = myUserHandle(); mApp1Provider1 = createAppWidgetProviderInfo( @@ -114,16 +103,12 @@ public final class WidgetsPredicationUpdateTaskTest { .collect(Collectors.toList()); }).when(manager).getInstalledProvidersForPackage(any(), eq(myUserHandle())); - // 2 widgets, app4/provider1 & app5/provider1, have already been added to the workspace. - mModelHelper.initializeData("widgets_predication_update_task_data"); - + LauncherLayoutBuilder builder = new LauncherLayoutBuilder() + .atWorkspace(0, 1, 2).putWidget("app4", "provider1", 1, 1) + .atWorkspace(0, 1, 3).putWidget("app5", "provider1", 1, 1); + mModelHelper.setupDefaultLayoutProvider(builder); MAIN_EXECUTOR.submit(() -> mModelHelper.getModel().addCallbacks(mCallback)).get(); - MODEL_EXECUTOR.post(() -> mModelHelper.getBgDataModel().widgetsModel.update( - LauncherAppState.getInstance(mModelHelper.sandboxContext), - /* packageUser= */ null)); - - MODEL_EXECUTOR.submit(() -> { }).get(); - MAIN_EXECUTOR.submit(() -> { }).get(); + mModelHelper.loadModelSync(); } @After @@ -132,65 +117,72 @@ public final class WidgetsPredicationUpdateTaskTest { } @Test - public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder() - throws Exception { - // WHEN newPredicationTask is executed with app predication of 5 apps. - AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1", - mUserHandle); - AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "provider1", - mUserHandle); - AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className", - mUserHandle); - AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1", - mUserHandle); - AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1", - mUserHandle); - mModelHelper.executeTaskForTest( - newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1))) - .forEach(Runnable::run); - - // THEN only 2 widgets are returned because - // 1. app5/provider1 & app4/provider1 have already been added to workspace. They are - // excluded from the result. - // 2. app3 doesn't have a widget. - // 3. only 1 widget is picked from app1 because we only want to promote one widget per app. - List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items - .stream() - .map(itemInfo -> (PendingAddWidgetInfo) itemInfo) - .collect(Collectors.toList()); - assertThat(recommendedWidgets).hasSize(2); - assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1); - assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1); + public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder() { + // Run on model executor so that no other task runs in the middle. + runOnExecutorSync(MODEL_EXECUTOR, () -> { + // WHEN newPredicationTask is executed with app predication of 5 apps. + AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1", + mUserHandle); + AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "provider1", + mUserHandle); + AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className", + mUserHandle); + AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1", + mUserHandle); + AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1", + mUserHandle); + mCallback.mRecommendedWidgets = null; + mModelHelper.getModel().enqueueModelUpdateTask( + newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1))); + runOnExecutorSync(MAIN_EXECUTOR, () -> { }); + + // THEN only 2 widgets are returned because + // 1. app5/provider1 & app4/provider1 have already been added to workspace. They are + // excluded from the result. + // 2. app3 doesn't have a widget. + // 3. only 1 widget is picked from app1 because we only want to promote one widget per app. + List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items + .stream() + .map(itemInfo -> (PendingAddWidgetInfo) itemInfo) + .collect(Collectors.toList()); + assertThat(recommendedWidgets).hasSize(2); + assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1); + assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1); + }); } @Test - public void widgetsRecommendationRan_shouldReturnPackageWidgetsWhenEmpty() - throws Exception { - - // Not installed widget - AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider3", - mUserHandle); - // Not installed app - AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1", - mUserHandle); - // Workspace added widgets - AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1", - mUserHandle); - AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1", - mUserHandle); - mModelHelper.executeTaskForTest( - newWidgetsPredicationTask(List.of(widget5, widget3, widget4, widget1))) - .forEach(Runnable::run); - - // THEN only 2 widgets are returned because the launcher only filters out non-exist widgets. - List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items - .stream() - .map(itemInfo -> (PendingAddWidgetInfo) itemInfo) - .collect(Collectors.toList()); - assertThat(recommendedWidgets).hasSize(2); - // Another widget from the same package - assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2); - assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1); + public void widgetsRecommendationRan_shouldReturnPackageWidgetsWhenEmpty() { + runOnExecutorSync(MODEL_EXECUTOR, () -> { + + // Not installed widget + AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider3", + mUserHandle); + // Not installed app + AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1", + mUserHandle); + // Workspace added widgets + AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1", + mUserHandle); + AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1", + mUserHandle); + + mCallback.mRecommendedWidgets = null; + mModelHelper.getModel().enqueueModelUpdateTask( + newWidgetsPredicationTask(List.of(widget5, widget3, widget4, widget1))); + runOnExecutorSync(MAIN_EXECUTOR, () -> { }); + + // THEN only 2 widgets are returned because the launcher only filters out + // non-exist widgets. + List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items + .stream() + .map(itemInfo -> (PendingAddWidgetInfo) itemInfo) + .collect(Collectors.toList()); + assertThat(recommendedWidgets).hasSize(2); + // Another widget from the same package + assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2); + assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1); + }); } private void assertWidgetInfo( diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java new file mode 100644 index 0000000000..6c0d44da6c --- /dev/null +++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java @@ -0,0 +1,228 @@ +/* + * 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.launcher3.taskbar; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + +import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS; +import static com.android.launcher3.taskbar.TaskbarHoverToolTipController.HOVER_TOOL_TIP_REVEAL_START_DELAY; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.Display; +import android.view.MotionEvent; + +import androidx.test.filters.SmallTest; + +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.folder.Folder; +import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.model.data.FolderInfo; +import com.android.launcher3.util.ActivityContextWrapper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; + +/** + * Tests for TaskbarHoverToolTipController. + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class TaskbarHoverToolTipControllerTest extends TaskbarBaseTestCase { + + private TaskbarHoverToolTipController mTaskbarHoverToolTipController; + private TestableLooper mTestableLooper; + + @Mock private TaskbarView mTaskbarView; + @Mock private MotionEvent mMotionEvent; + @Mock private BubbleTextView mHoverBubbleTextView; + @Mock private FolderIcon mHoverFolderIcon; + @Mock private Display mDisplay; + @Mock private TaskbarDragLayer mTaskbarDragLayer; + private Folder mSpyFolderView; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + Context context = getApplicationContext(); + + doAnswer((Answer<Object>) invocation -> context.getSystemService( + (String) invocation.getArgument(0))) + .when(taskbarActivityContext).getSystemService(anyString()); + when(taskbarActivityContext.getResources()).thenReturn(context.getResources()); + when(taskbarActivityContext.getApplicationInfo()).thenReturn( + context.getApplicationInfo()); + when(taskbarActivityContext.getDragLayer()).thenReturn(mTaskbarDragLayer); + when(taskbarActivityContext.getMainLooper()).thenReturn(context.getMainLooper()); + when(taskbarActivityContext.getDisplay()).thenReturn(mDisplay); + + when(mTaskbarDragLayer.getChildCount()).thenReturn(1); + mSpyFolderView = spy(new Folder(new ActivityContextWrapper(context), null)); + when(mTaskbarDragLayer.getChildAt(anyInt())).thenReturn(mSpyFolderView); + doReturn(false).when(mSpyFolderView).isOpen(); + + when(mHoverBubbleTextView.getText()).thenReturn("tooltip"); + doAnswer((Answer<Void>) invocation -> { + Object[] args = invocation.getArguments(); + ((int[]) args[0])[0] = 0; + ((int[]) args[0])[1] = 0; + return null; + }).when(mHoverBubbleTextView).getLocationOnScreen(any(int[].class)); + when(mHoverBubbleTextView.getWidth()).thenReturn(100); + when(mHoverBubbleTextView.getHeight()).thenReturn(100); + + mHoverFolderIcon.mInfo = new FolderInfo(); + mHoverFolderIcon.mInfo.title = "tooltip"; + doAnswer((Answer<Void>) invocation -> { + Object[] args = invocation.getArguments(); + ((int[]) args[0])[0] = 0; + ((int[]) args[0])[1] = 0; + return null; + }).when(mHoverFolderIcon).getLocationOnScreen(any(int[].class)); + when(mHoverFolderIcon.getWidth()).thenReturn(100); + when(mHoverFolderIcon.getHeight()).thenReturn(100); + + when(mTaskbarView.getTop()).thenReturn(200); + + mTaskbarHoverToolTipController = new TaskbarHoverToolTipController( + taskbarActivityContext, mTaskbarView, mHoverBubbleTextView); + mTestableLooper = TestableLooper.get(this); + } + + @Test + public void onHover_hoverEnterIcon_revealToolTip() { + when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER); + when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER); + + boolean hoverHandled = + mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent); + + // Verify fullscreen is not set until the delayed runnable to reveal the tooltip has run + verify(taskbarActivityContext, never()).setTaskbarWindowFullscreen(true); + waitForIdleSync(); + assertThat(hoverHandled).isTrue(); + verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, + true); + verify(taskbarActivityContext).setTaskbarWindowFullscreen(true); + } + + @Test + public void onHover_hoverExitIcon_closeToolTip() { + when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_EXIT); + when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_EXIT); + + boolean hoverHandled = + mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent); + waitForIdleSync(); + + assertThat(hoverHandled).isTrue(); + verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, + false); + } + + @Test + public void onHover_hoverEnterFolderIcon_revealToolTip() { + when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER); + when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER); + + boolean hoverHandled = + mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent); + + // Verify fullscreen is not set until the delayed runnable to reveal the tooltip has run + verify(taskbarActivityContext, never()).setTaskbarWindowFullscreen(true); + waitForIdleSync(); + assertThat(hoverHandled).isTrue(); + verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, + true); + verify(taskbarActivityContext).setTaskbarWindowFullscreen(true); + } + + @Test + public void onHover_hoverExitFolderIcon_closeToolTip() { + when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_EXIT); + when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_EXIT); + + boolean hoverHandled = + mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent); + waitForIdleSync(); + + assertThat(hoverHandled).isTrue(); + verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, + false); + } + + @Test + public void onHover_hoverExitFolderOpen_closeToolTip() { + when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_EXIT); + when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_EXIT); + doReturn(true).when(mSpyFolderView).isOpen(); + + boolean hoverHandled = + mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent); + waitForIdleSync(); + + assertThat(hoverHandled).isTrue(); + verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, + false); + } + + @Test + public void onHover_hoverEnterFolderOpen_noToolTip() { + when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER); + when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER); + doReturn(true).when(mSpyFolderView).isOpen(); + + boolean hoverHandled = + mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent); + + assertThat(hoverHandled).isFalse(); + } + + @Test + public void onHover_hoverMove_noUpdate() { + when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_MOVE); + when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_MOVE); + + boolean hoverHandled = + mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent); + + assertThat(hoverHandled).isFalse(); + } + + private void waitForIdleSync() { + mTestableLooper.moveTimeForward(HOVER_TOOL_TIP_REVEAL_START_DELAY + 1); + mTestableLooper.processAllMessages(); + } +} diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java index 962261940c..58be345fab 100644 --- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java +++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java @@ -17,6 +17,7 @@ import static com.android.quickstep.OverviewCommandHelper.TYPE_TOGGLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -26,12 +27,14 @@ import static org.mockito.Mockito.when; import android.os.Handler; import android.view.View; +import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.launcher3.logging.StatsLogManager; import com.android.quickstep.OverviewCommandHelper; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TouchInteractionService; +import com.android.quickstep.util.AssistUtils; import org.junit.Before; import org.junit.Test; @@ -53,6 +56,8 @@ public class TaskbarNavButtonControllerTest { @Mock Handler mockHandler; @Mock + AssistUtils mockAssistUtils; + @Mock StatsLogManager mockStatsLogManager; @Mock StatsLogManager.StatsLogger mockStatsLogger; @@ -70,12 +75,15 @@ public class TaskbarNavButtonControllerTest { MockitoAnnotations.initMocks(this); when(mockService.getDisplayId()).thenReturn(DISPLAY_ID); when(mockService.getOverviewCommandHelper()).thenReturn(mockCommandHelper); + when(mockService.getApplicationContext()) + .thenReturn(InstrumentationRegistry.getInstrumentation().getTargetContext() + .getApplicationContext()); when(mockStatsLogManager.logger()).thenReturn(mockStatsLogger); when(mockTaskbarControllers.getTaskbarActivityContext()) .thenReturn(mockTaskbarActivityContext); doReturn(mockStatsLogManager).when(mockTaskbarActivityContext).getStatsLogManager(); mNavButtonController = new TaskbarNavButtonController(mockService, - mockSystemUiProxy, mockHandler); + mockSystemUiProxy, mockHandler, mockAssistUtils); } @Test @@ -104,16 +112,42 @@ public class TaskbarNavButtonControllerTest { } @Test - public void testLongPressHome_enabled() { + public void testLongPressHome_enabled_withoutOverride() { mNavButtonController.setAssistantLongPressEnabled(true /*assistantLongPressEnabled*/); + when(mockAssistUtils.tryStartAssistOverride(anyInt())).thenReturn(false); + mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView); + verify(mockAssistUtils, times(1)).tryStartAssistOverride(anyInt()); verify(mockSystemUiProxy, times(1)).startAssistant(any()); } @Test - public void testLongPressHome_disabled() { + public void testLongPressHome_enabled_withOverride() { + mNavButtonController.setAssistantLongPressEnabled(true /*assistantLongPressEnabled*/); + when(mockAssistUtils.tryStartAssistOverride(anyInt())).thenReturn(true); + + mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView); + verify(mockAssistUtils, times(1)).tryStartAssistOverride(anyInt()); + verify(mockSystemUiProxy, never()).startAssistant(any()); + } + + @Test + public void testLongPressHome_disabled_withoutOverride() { mNavButtonController.setAssistantLongPressEnabled(false /*assistantLongPressEnabled*/); + when(mockAssistUtils.tryStartAssistOverride(anyInt())).thenReturn(false); + + mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView); + verify(mockAssistUtils, never()).tryStartAssistOverride(anyInt()); + verify(mockSystemUiProxy, never()).startAssistant(any()); + } + + @Test + public void testLongPressHome_disabled_withOverride() { + mNavButtonController.setAssistantLongPressEnabled(false /*assistantLongPressEnabled*/); + when(mockAssistUtils.tryStartAssistOverride(anyInt())).thenReturn(true); + mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView); + verify(mockAssistUtils, never()).tryStartAssistOverride(anyInt()); verify(mockSystemUiProxy, never()).startAssistant(any()); } diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt index 236b5db10c..3920b08030 100644 --- a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt +++ b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt @@ -1,6 +1,9 @@ package com.android.launcher3.taskbar.navbutton import android.content.res.Resources +import android.view.Surface +import android.view.Surface.ROTATION_270 +import android.view.Surface.Rotation import android.view.View import android.view.ViewGroup import android.widget.FrameLayout @@ -32,6 +35,8 @@ class NavButtonLayoutFactoryTest { @Mock lateinit var mockRecentsButton: ImageView @Mock lateinit var mockHomeButton: ImageView + private var surfaceRotation = Surface.ROTATION_0 + @Before fun setup() { MockitoAnnotations.initMocks(this) @@ -60,7 +65,8 @@ class NavButtonLayoutFactoryTest { isKidsMode = true, isInSetup = false, isThreeButtonNav = false, - phoneMode = false + phoneMode = false, + surfaceRotation = surfaceRotation ) assert(layoutter is KidsNavLayoutter) } @@ -74,7 +80,8 @@ class NavButtonLayoutFactoryTest { isKidsMode = false, isInSetup = true, isThreeButtonNav = false, - phoneMode = false + phoneMode = false, + surfaceRotation = surfaceRotation ) assert(layoutter is SetupNavLayoutter) } @@ -88,7 +95,8 @@ class NavButtonLayoutFactoryTest { isKidsMode = false, isInSetup = false, isThreeButtonNav = false, - phoneMode = false + phoneMode = false, + surfaceRotation = surfaceRotation ) assert(layoutter is TaskbarNavLayoutter) } @@ -101,7 +109,8 @@ class NavButtonLayoutFactoryTest { isKidsMode = false, isInSetup = false, isThreeButtonNav = false, - phoneMode = false + phoneMode = false, + surfaceRotation = surfaceRotation ) } @@ -114,7 +123,8 @@ class NavButtonLayoutFactoryTest { isKidsMode = false, isInSetup = false, isThreeButtonNav = true, - phoneMode = true + phoneMode = true, + surfaceRotation = surfaceRotation ) assert(layoutter is PhonePortraitNavLayoutter) } @@ -129,11 +139,28 @@ class NavButtonLayoutFactoryTest { isKidsMode = false, isInSetup = false, isThreeButtonNav = true, - phoneMode = true + phoneMode = true, + surfaceRotation = surfaceRotation ) assert(layoutter is PhoneLandscapeNavLayoutter) } + @Test + fun getTaskbarSeascapeLayoutter() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = false + setDeviceProfileLandscape() + val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = + getLayoutter( + isKidsMode = false, + isInSetup = false, + isThreeButtonNav = true, + phoneMode = true, + surfaceRotation = ROTATION_270 + ) + assert(layoutter is PhoneSeascapeNavLayoutter) + } + @Test(expected = IllegalStateException::class) fun noValidLayoutForPhoneGestureNav() { assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) @@ -142,7 +169,8 @@ class NavButtonLayoutFactoryTest { isKidsMode = false, isInSetup = false, isThreeButtonNav = false, - phoneMode = true + phoneMode = true, + surfaceRotation = surfaceRotation ) } @@ -157,7 +185,8 @@ class NavButtonLayoutFactoryTest { isKidsMode: Boolean, isInSetup: Boolean, isThreeButtonNav: Boolean, - phoneMode: Boolean + phoneMode: Boolean, + @Rotation surfaceRotation: Int ): NavButtonLayoutFactory.NavButtonLayoutter { return NavButtonLayoutFactory.getUiLayoutter( deviceProfile = mockDeviceProfile, @@ -166,7 +195,8 @@ class NavButtonLayoutFactoryTest { isKidsMode = isKidsMode, isInSetup = isInSetup, isThreeButtonNav = isThreeButtonNav, - phoneMode = phoneMode + phoneMode = phoneMode, + surfaceRotation = surfaceRotation ) } } diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java index 59f9d5f61d..3a5fb04f66 100644 --- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java +++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java @@ -16,11 +16,17 @@ package com.android.quickstep; +import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; + import static org.junit.Assert.assertTrue; import android.os.SystemProperties; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.Until; + import com.android.launcher3.Launcher; +import com.android.launcher3.tapl.LaunchedAppState; import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType; import com.android.launcher3.ui.AbstractLauncherUiTest; @@ -76,6 +82,21 @@ public abstract class AbstractQuickStepTest extends AbstractLauncherUiTest { } } + protected void assertTestActivityIsRunning(int activityNumber, String message) { + assertTrue(message, mDevice.wait( + Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity" + activityNumber)), + DEFAULT_UI_TIMEOUT)); + } + + protected LaunchedAppState getAndAssertLaunchedApp() { + final LaunchedAppState launchedAppState = mLauncher.getLaunchedAppState(); + executeOnLauncher(launcher -> assertTrue( + "Launcher activity is the top activity; expecting another activity to be the top " + + "one", + isInLaunchedApp(launcher))); + return launchedAppState; + } + private boolean isInLiveTileMode(Launcher launcher, LauncherInstrumentation.ContainerType expectedContainerType) { if (expectedContainerType != LauncherInstrumentation.ContainerType.OVERVIEW) { diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java index 97e34c5f10..a67d787842 100644 --- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java +++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java @@ -116,12 +116,13 @@ public class FallbackRecentsTest { Utilities.enableRunningInTestHarnessForTests(); } - final ViewCaptureRule viewCaptureRule = new ViewCaptureRule(); + final ViewCaptureRule viewCaptureRule = new ViewCaptureRule( + RecentsActivity.ACTIVITY_TRACKER::getCreatedActivity); mOrderSensitiveRules = RuleChain .outerRule(new SamplerRule()) .around(new NavigationModeSwitchRule(mLauncher)) - .around(viewCaptureRule) - .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture())); + .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData)) + .around(viewCaptureRule); mOtherLauncherActivity = context.getPackageManager().queryIntentActivities( getHomeIntentInPackage(context), diff --git a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt index a9dc043535..bb1afdf02d 100644 --- a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt +++ b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt @@ -15,6 +15,7 @@ */ package com.android.quickstep +import android.content.Context import android.graphics.Rect import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -29,7 +30,9 @@ import kotlin.math.roundToInt import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mockito.doReturn import org.mockito.Mockito.mock +import org.mockito.Mockito.spy /** Test for FullscreenDrawParams class. */ @SmallTest @@ -186,4 +189,76 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() { val expectedRadius = QuickStepContract.getWindowCornerRadius(context) assertThat(params.mCurrentDrawnCornerRadius).isEqualTo(expectedRadius) } + + @Test + fun setStartProgress_correctCornerRadiusForMultiDisplay() { + val display1Context = context + val display2Context = mock(Context::class.java) + val spyParams = spy(params) + + val display1TaskRadius = TaskCornerRadius.get(display1Context) + val display1WindowRadius = QuickStepContract.getWindowCornerRadius(display1Context) + val display2TaskRadius = display1TaskRadius * 2 + 1 // Arbitrarily different. + val display2WindowRadius = display1WindowRadius * 2 + 1 // Arbitrarily different. + doReturn(display2TaskRadius).`when`(spyParams).computeTaskCornerRadius(display2Context) + doReturn(display2WindowRadius).`when`(spyParams).computeWindowCornerRadius(display2Context) + + spyParams.updateCornerRadius(display1Context) + spyParams.setProgress( + /* fullscreenProgress= */ 0f, + /* parentScale= */ 1.0f, + /* taskViewScale= */ 1.0f, + /* unused previewWidth= */ -1, + /* unusedDp= */ null, + /* unused previewPositionHelper= */ null + ) + assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display1TaskRadius) + + spyParams.updateCornerRadius(display2Context) + spyParams.setProgress( + /* fullscreenProgress= */ 0f, + /* parentScale= */ 1.0f, + /* taskViewScale= */ 1.0f, + /* unused previewWidth= */ -1, + /* unusedDp= */ null, + /* unused previewPositionHelper= */ null + ) + assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display2TaskRadius) + } + + @Test + fun setFullProgress_correctCornerRadiusForMultiDisplay() { + val display1Context = context + val display2Context = mock(Context::class.java) + val spyParams = spy(params) + + val display1TaskRadius = TaskCornerRadius.get(display1Context) + val display1WindowRadius = QuickStepContract.getWindowCornerRadius(display1Context) + val display2TaskRadius = display1TaskRadius * 2 + 1 // Arbitrarily different. + val display2WindowRadius = display1WindowRadius * 2 + 1 // Arbitrarily different. + doReturn(display2TaskRadius).`when`(spyParams).computeTaskCornerRadius(display2Context) + doReturn(display2WindowRadius).`when`(spyParams).computeWindowCornerRadius(display2Context) + + spyParams.updateCornerRadius(display1Context) + spyParams.setProgress( + /* fullscreenProgress= */ 1.0f, + /* parentScale= */ 1.0f, + /* taskViewScale= */ 1.0f, + /* unused previewWidth= */ -1, + /* unusedDp= */ null, + /* unused previewPositionHelper= */ null + ) + assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display1WindowRadius) + + spyParams.updateCornerRadius(display2Context) + spyParams.setProgress( + /* fullscreenProgress= */ 1.0f, + /* parentScale= */ 1.0f, + /* taskViewScale= */ 1.0f, + /* unused previewWidth= */ -1, + /* unusedDp= */ null, + /* unused previewPositionHelper= */ null + ) + assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display2WindowRadius) + } } diff --git a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java index 9c240f08c7..298dd6cae0 100644 --- a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java +++ b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java @@ -53,6 +53,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; +import java.util.List; + @SmallTest @RunWith(AndroidJUnit4.class) public class OrientationTouchTransformerTest { @@ -296,7 +298,7 @@ public class OrientationTouchTransformerTest { WindowManagerProxy wmProxy = mock(WindowManagerProxy.class); doReturn(cachedDisplayInfo).when(wmProxy).getDisplayInfo(any()); doReturn(windowBounds).when(wmProxy).getRealBounds(any(), any()); - ArrayMap<CachedDisplayInfo, WindowBounds[]> internalDisplayBounds = new ArrayMap<>(); + ArrayMap<CachedDisplayInfo, List<WindowBounds>> internalDisplayBounds = new ArrayMap<>(); doReturn(internalDisplayBounds).when(wmProxy).estimateInternalDisplayBounds(any()); return new DisplayController.Info( getApplicationContext(), wmProxy, new ArrayMap<>()); diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java index df5303f4f4..20aa410f8f 100644 --- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java +++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java @@ -16,15 +16,17 @@ package com.android.quickstep; +import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; +import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; + import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import com.android.launcher3.ui.TaplTestsLauncher3; -import com.android.launcher3.util.RaceConditionReproducer; +import com.android.launcher3.util.rule.TestStabilityRule.Stability; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,21 +47,9 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest { startTestActivity(2); } - private void runTest(String... eventSequence) { - final RaceConditionReproducer eventProcessor = new RaceConditionReproducer(eventSequence); - - // Destroy Launcher activity. - closeLauncherActivity(); - - // The test action. - eventProcessor.startIteration(); - mLauncher.goHome(); - eventProcessor.finishIteration(); - } - - @Ignore @Test @NavigationModeSwitch + @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/187761685 public void testStressPressHome() { for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) { // Destroy Launcher activity. @@ -70,9 +60,9 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest { } } - @Ignore @Test @NavigationModeSwitch + @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/187761685 public void testStressSwipeToOverview() { for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) { // Destroy Launcher activity. diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index 32eadcea61..25f90caa3e 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -16,7 +16,10 @@ package com.android.quickstep; +import static com.android.launcher3.testing.shared.TestProtocol.FLAKY_QUICK_SWITCH_TO_PREVIOUS_APP; import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; +import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; +import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT; import static org.junit.Assert.assertEquals; @@ -27,6 +30,7 @@ import static org.junit.Assume.assumeTrue; import android.content.Intent; import android.platform.test.annotations.PlatinumTest; +import android.util.Log; import androidx.test.filters.LargeTest; import androidx.test.platform.app.InstrumentationRegistry; @@ -42,9 +46,13 @@ import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel; import com.android.launcher3.tapl.Overview; import com.android.launcher3.tapl.OverviewActions; import com.android.launcher3.tapl.OverviewTask; +import com.android.launcher3.tapl.OverviewTaskMenu; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; import com.android.launcher3.ui.TaplTestsLauncher3; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; +import com.android.launcher3.util.rule.TestStabilityRule; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch; import com.android.quickstep.views.RecentsView; @@ -143,7 +151,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { assertNotNull("overview.getCurrentTask() returned null (1)", task); assertNotNull("OverviewTask.open returned null", task.open()); assertTrue("Test activity didn't open from Overview", mDevice.wait(Until.hasObject( - By.pkg(getAppPackageName()).text("TestActivity2")), + By.pkg(getAppPackageName()).text("TestActivity2")), DEFAULT_UI_TIMEOUT)); executeOnLauncher(launcher -> assertTrue( "Launcher activity is the top activity; expecting another activity to be the top " @@ -177,7 +185,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @NavigationModeSwitch @PortraitLandscape - @ScreenRecord // b/195673272 @PlatinumTest(focusArea = "launcher") public void testOverviewActions() throws Exception { // Experimenting for b/165029151: @@ -192,6 +199,19 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { actionsView.clickAndDismissScreenshot(); } + + @Test + public void testOverviewActionsMenu() throws Exception { + startTestAppsWithCheck(); + + OverviewTaskMenu menu = mLauncher.goHome().switchToOverview().getCurrentTask().tapMenu(); + + assertNotNull("Tapping App info menu item returned null", menu.tapAppInfoMenuItem()); + executeOnLauncher(launcher -> assertTrue( + "Launcher activity is the top activity; expecting another activity to be the top", + isInLaunchedApp(launcher))); + } + private int getCurrentOverviewPage(Launcher launcher) { return launcher.<RecentsView>getOverviewPanel().getCurrentPage(); } @@ -237,16 +257,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { isInState(() -> LauncherState.OVERVIEW)); } - private LaunchedAppState getAndAssertLaunchedApp() { - final LaunchedAppState launchedAppState = mLauncher.getLaunchedAppState(); - assertNotNull("Launcher.getLaunchedApp() returned null", launchedAppState); - executeOnLauncher(launcher -> assertTrue( - "Launcher activity is the top activity; expecting another activity to be the top " - + "one", - isInLaunchedApp(launcher))); - return launchedAppState; - } - private void quickSwitchToPreviousAppAndAssert(boolean toRight) { final LaunchedAppState launchedAppState = getAndAssertLaunchedApp(); if (toRight) { @@ -288,22 +298,22 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { startTestActivity(4); quickSwitchToPreviousAppAndAssert(true /* toRight */); - assertTrue("The first app we should have quick switched to is not running", - isTestActivityRunning(3)); + assertTestActivityIsRunning(3, + "The first app we should have quick switched to is not running"); quickSwitchToPreviousAppAndAssert(true /* toRight */); if (mLauncher.getNavigationModel() == NavigationModel.THREE_BUTTON) { // 3-button mode toggles between 2 apps, rather than going back further. - assertTrue("Second quick switch should have returned to the first app.", - isTestActivityRunning(4)); + assertTestActivityIsRunning(4, + "Second quick switch should have returned to the first app."); } else { - assertTrue("The second app we should have quick switched to is not running", - isTestActivityRunning(2)); + assertTestActivityIsRunning(2, + "The second app we should have quick switched to is not running"); } quickSwitchToPreviousAppAndAssert(false /* toRight */); - assertTrue("The 2nd app we should have quick switched to is not running", - isTestActivityRunning(3)); + assertTestActivityIsRunning(3, + "The 2nd app we should have quick switched to is not running"); final LaunchedAppState launchedAppState = getAndAssertLaunchedApp(); launchedAppState.switchToOverview(); @@ -312,7 +322,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @ScreenRecord // b/242163205 @PlatinumTest(focusArea = "launcher") - @TaskbarModeSwitch(mode = PERSISTENT) + @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/286084688 public void testQuickSwitchToPreviousAppForTablet() throws Exception { assumeTrue(mLauncher.isTablet()); startTestActivity(2); @@ -328,17 +338,20 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { // Quick-switch to the test app with swiping to right. quickSwitchToPreviousAppAndAssert(true /* toRight */); - assertTrue("The first app we should have quick switched to is not running", - isTestActivityRunning(2)); + assertTestActivityIsRunning(2, + "The first app we should have quick switched to is not running"); // Expect task bar visible when the launched app was the test activity. launchedAppState = getAndAssertLaunchedApp(); - launchedAppState.assertTaskbarVisible(); - } - private boolean isTestActivityRunning(int activityNumber) { - return mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()) - .text("TestActivity" + activityNumber)), - DEFAULT_UI_TIMEOUT); + Log.e(FLAKY_QUICK_SWITCH_TO_PREVIOUS_APP, + "is Taskbar Transient : " + DisplayController.isTransientTaskbar(mTargetContext)); + // TODO(b/286084688): Remove this branching check after test corruption is resolved. + // Branching this check because of test corruption. + if (DisplayController.isTransientTaskbar(mTargetContext)) { + launchedAppState.assertTaskbarHidden(); + } else { + launchedAppState.assertTaskbarVisible(); + } } @Test @@ -348,8 +361,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { public void testQuickSwitchFromHome() throws Exception { startTestActivity(2); mLauncher.goHome().quickSwitchToPreviousApp(); - assertTrue("The most recent task is not running after quick switching from home", - isTestActivityRunning(2)); + assertTestActivityIsRunning(2, + "The most recent task is not running after quick switching from home"); getAndAssertLaunchedApp(); } diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java index e8cadabe72..92b598b626 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java @@ -15,25 +15,41 @@ */ package com.android.quickstep; + +import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; +import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import android.content.Intent; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.LargeTest; +import androidx.test.platform.app.InstrumentationRegistry; + import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; import com.android.launcher3.ui.TaplTestsLauncher3; +import com.android.launcher3.util.rule.TestStabilityRule; import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +@LargeTest +@RunWith(AndroidJUnit4.class) public class TaplTestsSplitscreen extends AbstractQuickStepTest { private static final String CALCULATOR_APP_NAME = "Calculator"; private static final String CALCULATOR_APP_PACKAGE = resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR); + private static final String READ_DEVICE_CONFIG_PERMISSION = + "android.permission.READ_DEVICE_CONFIG"; + @Override @Before public void setUp() throws Exception { @@ -44,6 +60,8 @@ public class TaplTestsSplitscreen extends AbstractQuickStepTest { mLauncher.enableBlockTimeout(true); mLauncher.showTaskbarIfHidden(); } + InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( + READ_DEVICE_CONFIG_PERMISSION); } @After @@ -62,6 +80,7 @@ public class TaplTestsSplitscreen extends AbstractQuickStepTest { @Test @PortraitLandscape @TaskbarModeSwitch + @TestStabilityRule.Stability(flavors = PLATFORM_POSTSUBMIT | LOCAL) // b/295225524 public void testSplitAppFromHomeWithItself() throws Exception { // Currently only tablets have Taskbar in Overview, so test is only active on tablets assumeTrue(mLauncher.isTablet()); diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java index 40be4806ec..4ff2f9c721 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java @@ -20,6 +20,7 @@ import static com.android.quickstep.TaplTestsTaskbar.TaskbarMode.TRANSIENT; import androidx.test.filters.LargeTest; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; import org.junit.Test; diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java new file mode 100644 index 0000000000..907dbccb91 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep; + +import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; + +import android.app.Instrumentation; + +import androidx.test.filters.LargeTest; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.tapl.LauncherInstrumentation.TrackpadGestureType; +import com.android.launcher3.tapl.Workspace; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; +import com.android.launcher3.ui.TaplTestsLauncher3; +import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@LargeTest +@RunWith(AndroidJUnit4.class) +public class TaplTestsTrackpad extends AbstractQuickStepTest { + + private static final String READ_DEVICE_CONFIG_PERMISSION = + "android.permission.READ_DEVICE_CONFIG"; + + @Before + public void setUp() throws Exception { + super.setUp(); + TaplTestsLauncher3.initialize(this); + } + + @After + public void tearDown() { + mLauncher.setTrackpadGestureType(TrackpadGestureType.NONE); + } + + @Test + @PortraitLandscape + @NavigationModeSwitch + public void goHome() throws Exception { + assumeTrue(mLauncher.isTablet()); + + mLauncher.setTrackpadGestureType(TrackpadGestureType.THREE_FINGER); + startTestActivity(2); + mLauncher.goHome(); + } + + @Test + @PortraitLandscape + // TODO(b/291944684): Support back in 3-button mode. It requires triggering the logic to enable + // trackpad gesture back in SysUI. Normally it's triggered by the attachment of a trackpad. We + // need to figure out a way to emulate that in the test, or bypass the logic altogether. + @NavigationModeSwitch(mode = ZERO_BUTTON) + public void pressBack() throws Exception { + assumeTrue(mLauncher.isTablet()); + assumeFalse(FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get()); + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + + try { + instrumentation.getUiAutomation().adoptShellPermissionIdentity( + READ_DEVICE_CONFIG_PERMISSION); + mLauncher.setTrackpadGestureType(TrackpadGestureType.THREE_FINGER); + + startTestActivity(2); + mLauncher.pressBack(); + } finally { + instrumentation.getUiAutomation().dropShellPermissionIdentity(); + } + } + + @Test + @PortraitLandscape + @NavigationModeSwitch + public void switchToOverview() throws Exception { + assumeTrue(mLauncher.isTablet()); + + mLauncher.setTrackpadGestureType(TrackpadGestureType.THREE_FINGER); + startTestActivity(2); + mLauncher.goHome().switchToOverview(); + } + + @Test + @PortraitLandscape + @NavigationModeSwitch + public void testAllAppsFromHome() throws Exception { + assumeTrue(mLauncher.isTablet()); + + mLauncher.setTrackpadGestureType(TrackpadGestureType.TWO_FINGER); + assertNotNull("switchToAllApps() returned null", + mLauncher.getWorkspace().switchToAllApps()); + } + + @Test + @NavigationModeSwitch + @PortraitLandscape + public void testQuickSwitchFromHome() throws Exception { + assumeTrue(mLauncher.isTablet()); + + startTestActivity(2); + Workspace workspace = mLauncher.goHome(); + mLauncher.setTrackpadGestureType(TrackpadGestureType.FOUR_FINGER); + workspace.quickSwitchToPreviousApp(); + assertTestActivityIsRunning(2, + "The most recent task is not running after quick switching from home"); + getAndAssertLaunchedApp(); + } +} diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java index b58fe29a96..3869bf7642 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java @@ -18,7 +18,6 @@ package com.android.quickstep; import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES; import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT; - import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; @@ -64,4 +63,15 @@ public class TaplTestsTransientTaskbar extends AbstractTaplTestsTaskbar { throw new RuntimeException(e); } } + + @Test + @TaskbarModeSwitch(mode = TRANSIENT) + public void testClickHoveredTaskbarToGoHome() { + try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) { + getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE); + mLauncher.getLaunchedAppState().clickStashedTaskbarToGoHome(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java index 7e408a8587..8cc84875e0 100644 --- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java +++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java @@ -17,8 +17,8 @@ package com.android.quickstep; import static androidx.test.InstrumentationRegistry.getContext; import static androidx.test.InstrumentationRegistry.getInstrumentation; -import static androidx.test.InstrumentationRegistry.getTargetContext; +import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; import static com.android.launcher3.testcomponent.TestCommandReceiver.EXTRA_VALUE; import static com.android.launcher3.testcomponent.TestCommandReceiver.SET_LIST_VIEW_SERVICE_BINDER; import static com.android.launcher3.util.WidgetUtils.createWidgetInfo; @@ -32,7 +32,6 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.spy; import android.appwidget.AppWidgetManager; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -42,14 +41,16 @@ import android.view.View; import android.view.ViewConfiguration; import android.widget.RemoteViews; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; import androidx.test.filters.Suppress; -import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.By; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.Until; -import com.android.launcher3.LauncherSettings; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.celllayout.FavoriteItemsTransaction; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.tapl.LaunchedAppState; import com.android.launcher3.testcomponent.ListViewService; @@ -57,6 +58,7 @@ import com.android.launcher3.testcomponent.ListViewService.SimpleViewsFactory; import com.android.launcher3.testcomponent.TestCommandReceiver; import com.android.launcher3.ui.TaplTestsLauncher3; import com.android.launcher3.ui.TestViewHelpers; +import com.android.launcher3.util.Executors; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; @@ -67,6 +69,7 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.lang.reflect.Field; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.IntConsumer; /** @@ -84,9 +87,9 @@ import java.util.function.IntConsumer; @RunWith(AndroidJUnit4.class) public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest { - private ContentResolver mResolver; private SparseArray<ViewConfiguration> mConfigMap; private InitTracker mInitTracker; + private LauncherModel mModel; @Before public void setUp() throws Exception { @@ -101,8 +104,8 @@ public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest { TaplTestsLauncher3.initialize(this); - mResolver = mTargetContext.getContentResolver(); - LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); + mModel = LauncherAppState.getInstance(mTargetContext).getModel(); + Executors.MODEL_EXECUTOR.submit(mModel.getModelDbController()::createEmptyDB).get(); // Get static configuration map Field field = ViewConfiguration.class.getDeclaredField("sConfigurations"); @@ -182,26 +185,30 @@ public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest { private void executeSwipeUpTestWithWidget(IntConsumer widgetIdCreationCallback, IntConsumer updateBeforeSwipeUp, String finalWidgetText) { try { - // Clear all existing data - LauncherSettings.Settings.call(mResolver, - LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); - LauncherSettings.Settings.call(mResolver, - LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG); - LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false); + LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(false); + // Make sure the widget is big enough to show a list of items info.minSpanX = 2; info.minSpanY = 2; info.spanX = 2; info.spanY = 2; - LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true); + AtomicInteger widgetId = new AtomicInteger(); + new FavoriteItemsTransaction(mTargetContext) + .addItem(() -> { + LauncherAppWidgetInfo item = createWidgetInfo(info, mTargetContext, true); + item.screenId = FIRST_SCREEN_ID; + widgetId.set(item.appWidgetId); + return item; + }) + .commitAndLoadHome(mLauncher); + + - addItemToScreen(item); assertTrue("Widget is not present", mLauncher.goHome().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null); - int widgetId = item.appWidgetId; // Verify widget id - widgetIdCreationCallback.accept(widgetId); + widgetIdCreationCallback.accept(widgetId.get()); // Go to overview once so that all views are initialized and cached startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); @@ -214,7 +221,7 @@ public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest { LaunchedAppState launchedAppState = mLauncher.getLaunchedAppState(); // Update widget - updateBeforeSwipeUp.accept(widgetId); + updateBeforeSwipeUp.accept(widgetId.get()); launchedAppState.switchToOverview(); assertEquals("Views inflated during swipe up", 0, mInitTracker.viewInitCount); diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt new file mode 100644 index 0000000000..fc767fae72 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt @@ -0,0 +1,395 @@ +/* + * 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.quickstep.util + +import android.app.ActivityManager.RunningTaskInfo +import android.app.ActivityTaskManager.INVALID_TASK_ID +import android.content.Context +import android.content.ContextWrapper +import android.content.Intent +import android.os.UserHandle +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.model.data.ItemInfo +import com.android.launcher3.shortcuts.ShortcutKey +import com.android.launcher3.ui.AbstractLauncherUiTest +import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_PENDINGINTENT_PENDINGINTENT +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_PENDINGINTENT_TASK +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_SHORTCUT_TASK +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_SINGLE_INTENT_FULLSCREEN +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_SINGLE_SHORTCUT_FULLSCREEN +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_SINGLE_TASK_FULLSCREEN +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_TASK_PENDINGINTENT +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_TASK_SHORTCUT +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_TASK_TASK +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test + +class SplitSelectDataHolderTest { + private lateinit var splitSelectDataHolder: SplitSelectDataHolder + + private val context: Context = + ContextWrapper(InstrumentationRegistry.getInstrumentation().targetContext) + private val sampleTaskInfo = RunningTaskInfo() + private val sampleTaskId = 10 + private val sampleTaskId2 = 11 + private val sampleUser = UserHandle(0) + private val sampleIntent = Intent() + private val sampleIntent2 = Intent() + private val sampleShortcut = Intent() + private val sampleShortcut2 = Intent() + private val sampleItemInfo = ItemInfo() + private val samplePackage = + AbstractLauncherUiTest.resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR) + + @Before + fun setup() { + splitSelectDataHolder = SplitSelectDataHolder(context) + + sampleTaskInfo.taskId = sampleTaskId + sampleItemInfo.user = sampleUser + sampleIntent.setPackage(samplePackage) + sampleIntent2.setPackage(samplePackage) + sampleShortcut.setPackage(samplePackage) + sampleShortcut2.setPackage(samplePackage) + sampleShortcut.putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "sampleShortcut") + sampleShortcut2.putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "sampleShortcut2") + } + + @Test + fun setInitialAsTask() { + splitSelectDataHolder.setInitialTaskSelect( + sampleTaskInfo, + STAGE_POSITION_TOP_OR_LEFT, + null, + null + ) + assertTrue(splitSelectDataHolder.isSplitSelectActive()) + } + + @Test + fun setInitialAsIntent() { + splitSelectDataHolder.setInitialTaskSelect( + sampleIntent, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + assertTrue(splitSelectDataHolder.isSplitSelectActive()) + } + + @Test + fun setInitialAsIntentWithAlreadyRunningTask() { + splitSelectDataHolder.setInitialTaskSelect( + sampleIntent, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + sampleTaskId + ) + assertTrue(splitSelectDataHolder.isSplitSelectActive()) + } + + @Test + fun setInitialAsShortcut() { + splitSelectDataHolder.setInitialTaskSelect( + sampleShortcut, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + assertTrue(splitSelectDataHolder.isSplitSelectActive()) + } + + @Test + fun setSecondAsTask() { + splitSelectDataHolder.setInitialTaskSelect( + sampleShortcut, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + splitSelectDataHolder.setSecondTask(sampleTaskId) + assertTrue(splitSelectDataHolder.isBothSplitAppsConfirmed()) + } + + @Test + fun setSecondAsIntent() { + splitSelectDataHolder.setInitialTaskSelect( + sampleTaskInfo, + STAGE_POSITION_TOP_OR_LEFT, + null, + null + ) + splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser) + assertTrue(splitSelectDataHolder.isBothSplitAppsConfirmed()) + } + + @Test + fun setSecondAsShortcut() { + splitSelectDataHolder.setInitialTaskSelect( + sampleIntent, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + splitSelectDataHolder.setSecondTask(sampleShortcut, sampleUser) + assertTrue(splitSelectDataHolder.isBothSplitAppsConfirmed()) + } + + @Test + fun generateLaunchData_Task_Task() { + splitSelectDataHolder.setInitialTaskSelect( + sampleTaskInfo, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null + ) + splitSelectDataHolder.setSecondTask(sampleTaskId2) + val launchData = splitSelectDataHolder.getSplitLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_TASK_TASK) + + // should contain a valid task ID for first app, and no intent or shortcut + assertNotEquals(launchData.initialTaskId, INVALID_TASK_ID) + assertNull(launchData.initialPendingIntent) + assertNull(launchData.initialShortcut) + + // should contain a valid task ID for second app, and no intent or shortcut + assertNotEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondPendingIntent) + assertNull(launchData.secondShortcut) + } + + @Test + fun generateLaunchData_Task_Intent() { + splitSelectDataHolder.setInitialTaskSelect( + sampleTaskInfo, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null + ) + splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser) + val launchData = splitSelectDataHolder.getSplitLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_TASK_PENDINGINTENT) + + // should contain a valid task ID for first app, and no intent or shortcut + assertNotEquals(launchData.initialTaskId, INVALID_TASK_ID) + assertNull(launchData.initialPendingIntent) + assertNull(launchData.initialShortcut) + + // should contain a valid intent for second app, and no task ID or shortcut + assertNotNull(launchData.secondPendingIntent) + assertEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondShortcut) + } + + @Test + fun generateLaunchData_Task_Shortcut() { + splitSelectDataHolder.setInitialTaskSelect( + sampleTaskInfo, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null + ) + splitSelectDataHolder.setSecondTask(sampleShortcut, sampleUser) + val launchData = splitSelectDataHolder.getSplitLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_TASK_SHORTCUT) + + // should contain a valid task ID for first app, and no intent or shortcut + assertNotEquals(launchData.initialTaskId, INVALID_TASK_ID) + assertNull(launchData.initialPendingIntent) + assertNull(launchData.initialShortcut) + + // should contain a valid shortcut and intent for second app, and no task ID + assertNotNull(launchData.secondShortcut) + assertNotNull(launchData.secondPendingIntent) + assertEquals(launchData.secondTaskId, INVALID_TASK_ID) + } + + @Test + fun generateLaunchData_Intent_Task() { + splitSelectDataHolder.setInitialTaskSelect( + sampleIntent, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + splitSelectDataHolder.setSecondTask(sampleTaskId) + val launchData = splitSelectDataHolder.getSplitLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_PENDINGINTENT_TASK) + + // should contain a valid intent for first app, and no task ID or shortcut + assertNotNull(launchData.initialPendingIntent) + assertEquals(launchData.initialTaskId, INVALID_TASK_ID) + assertNull(launchData.initialShortcut) + + // should contain a valid task ID for second app, and no intent or shortcut + assertNotEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondPendingIntent) + assertNull(launchData.secondShortcut) + } + + @Test + fun generateLaunchData_Shortcut_Task() { + splitSelectDataHolder.setInitialTaskSelect( + sampleShortcut, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + splitSelectDataHolder.setSecondTask(sampleTaskId) + val launchData = splitSelectDataHolder.getSplitLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_SHORTCUT_TASK) + + // should contain a valid shortcut and intent for first app, and no task ID + assertNotNull(launchData.initialShortcut) + assertNotNull(launchData.initialPendingIntent) + assertEquals(launchData.initialTaskId, INVALID_TASK_ID) + + // should contain a valid task ID for second app, and no intent or shortcut + assertNotEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondPendingIntent) + assertNull(launchData.secondShortcut) + } + + @Test + fun generateLaunchData_Intent_Intent() { + splitSelectDataHolder.setInitialTaskSelect( + sampleIntent, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + splitSelectDataHolder.setSecondTask(sampleIntent2, sampleUser) + val launchData = splitSelectDataHolder.getSplitLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_PENDINGINTENT_PENDINGINTENT) + + // should contain a valid intent for first app, and no task ID or shortcut + assertNotNull(launchData.initialPendingIntent) + assertEquals(launchData.initialTaskId, INVALID_TASK_ID) + assertNull(launchData.initialShortcut) + + // should contain a valid intent for second app, and no task ID or shortcut + assertNotNull(launchData.secondPendingIntent) + assertEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondShortcut) + } + + @Test + fun generateLaunchData_Single_Task() { + splitSelectDataHolder.setInitialTaskSelect( + sampleTaskInfo, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null + ) + val launchData = splitSelectDataHolder.getFullscreenLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_SINGLE_TASK_FULLSCREEN) + + // should contain a valid task ID for first app, and no intent or shortcut + assertNotEquals(launchData.initialTaskId, INVALID_TASK_ID) + assertNull(launchData.initialPendingIntent) + assertNull(launchData.initialShortcut) + + // should contain no task ID, intent, or shortcut for second app + assertEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondPendingIntent) + assertNull(launchData.secondShortcut) + } + + @Test + fun generateLaunchData_Single_Intent() { + splitSelectDataHolder.setInitialTaskSelect( + sampleIntent, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + val launchData = splitSelectDataHolder.getFullscreenLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_SINGLE_INTENT_FULLSCREEN) + + // should contain a valid intent for first app, and no task ID or shortcut + assertNotNull(launchData.initialPendingIntent) + assertEquals(launchData.initialTaskId, INVALID_TASK_ID) + assertNull(launchData.initialShortcut) + + // should contain no task ID, intent, or shortcut for second app + assertEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondPendingIntent) + assertNull(launchData.secondShortcut) + } + + @Test + fun generateLaunchData_Single_Shortcut() { + splitSelectDataHolder.setInitialTaskSelect( + sampleShortcut, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + val launchData = splitSelectDataHolder.getFullscreenLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_SINGLE_SHORTCUT_FULLSCREEN) + + // should contain a valid shortcut and intent for first app, and no task ID + assertNotNull(launchData.initialShortcut) + assertNotNull(launchData.initialPendingIntent) + assertEquals(launchData.initialTaskId, INVALID_TASK_ID) + + // should contain no task ID, intent, or shortcut for second app + assertEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondPendingIntent) + assertNull(launchData.secondShortcut) + } + + @Test + fun clearState() { + splitSelectDataHolder.setInitialTaskSelect( + sampleTaskInfo, + STAGE_POSITION_TOP_OR_LEFT, + null, + null + ) + splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser) + splitSelectDataHolder.resetState() + assertFalse(splitSelectDataHolder.isSplitSelectActive()) + } +} diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt index 0b3c0e8945..69109c2c45 100644 --- a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt +++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt @@ -37,6 +37,7 @@ import com.android.launcher3.util.withArgCaptor import com.android.quickstep.RecentsModel import com.android.quickstep.SystemUiProxy import com.android.systemui.shared.recents.model.Task +import java.util.function.Consumer import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNull @@ -48,7 +49,6 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations -import java.util.function.Consumer @RunWith(AndroidJUnit4::class) class SplitSelectStateControllerTest { @@ -67,6 +67,9 @@ class SplitSelectStateControllerTest { private val primaryUserHandle = UserHandle(ActivityManager.RunningTaskInfo().userId) private val nonPrimaryUserHandle = UserHandle(ActivityManager.RunningTaskInfo().userId + 10) + private var taskIdCounter = 0 + private fun getUniqueId(): Int { return ++taskIdCounter } + @Before fun setup() { MockitoAnnotations.initMocks(this) @@ -100,15 +103,15 @@ class SplitSelectStateControllerTest { tasks.add(groupTask2) // Assertions happen in the callback we get from what we pass into - // #findLastActiveTaskAndRunCallback + // #findLastActiveTasksAndRunCallback val taskConsumer = - Consumer<Task> { assertNull("No tasks should have matched", it /*task*/) } + Consumer<List<Task>> { assertNull("No tasks should have matched", it[0] /*task*/) } // Capture callback from recentsModel#getTasks() val consumer = withArgCaptor<Consumer<ArrayList<GroupTask>>> { - splitSelectStateController.findLastActiveTaskAndRunCallback( - nonMatchingComponent, + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(nonMatchingComponent), taskConsumer ) verify(recentsModel).getTasks(capture()) @@ -139,27 +142,27 @@ class SplitSelectStateControllerTest { tasks.add(groupTask2) // Assertions happen in the callback we get from what we pass into - // #findLastActiveTaskAndRunCallback + // #findLastActiveTasksAndRunCallback val taskConsumer = - Consumer<Task> { + Consumer<List<Task>> { assertEquals( "ComponentName package mismatched", - it.key.baseIntent.component?.packageName, + it[0].key.baseIntent.component?.packageName, matchingPackage ) assertEquals( "ComponentName class mismatched", - it.key.baseIntent.component?.className, + it[0].key.baseIntent.component?.className, matchingClass ) - assertEquals(it, groupTask1.task1) + assertEquals(it[0], groupTask1.task1) } // Capture callback from recentsModel#getTasks() val consumer = withArgCaptor<Consumer<ArrayList<GroupTask>>> { - splitSelectStateController.findLastActiveTaskAndRunCallback( - matchingComponent, + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(matchingComponent), taskConsumer ) verify(recentsModel).getTasks(capture()) @@ -190,15 +193,15 @@ class SplitSelectStateControllerTest { tasks.add(groupTask2) // Assertions happen in the callback we get from what we pass into - // #findLastActiveTaskAndRunCallback + // #findLastActiveTasksAndRunCallback val taskConsumer = - Consumer<Task> { assertNull("No tasks should have matched", it /*task*/) } + Consumer<List<Task>> { assertNull("No tasks should have matched", it[0] /*task*/) } // Capture callback from recentsModel#getTasks() val consumer = withArgCaptor<Consumer<ArrayList<GroupTask>>> { - splitSelectStateController.findLastActiveTaskAndRunCallback( - nonPrimaryUserComponent, + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(nonPrimaryUserComponent), taskConsumer ) verify(recentsModel).getTasks(capture()) @@ -231,28 +234,28 @@ class SplitSelectStateControllerTest { tasks.add(groupTask2) // Assertions happen in the callback we get from what we pass into - // #findLastActiveTaskAndRunCallback + // #findLastActiveTasksAndRunCallback val taskConsumer = - Consumer<Task> { + Consumer<List<Task>> { assertEquals( "ComponentName package mismatched", - it.key.baseIntent.component?.packageName, + it[0].key.baseIntent.component?.packageName, matchingPackage ) assertEquals( "ComponentName class mismatched", - it.key.baseIntent.component?.className, + it[0].key.baseIntent.component?.className, matchingClass ) - assertEquals("userId mismatched", it.key.userId, nonPrimaryUserHandle.identifier) - assertEquals(it, groupTask1.task1) + assertEquals("userId mismatched", it[0].key.userId, nonPrimaryUserHandle.identifier) + assertEquals(it[0], groupTask1.task1) } // Capture callback from recentsModel#getTasks() val consumer = withArgCaptor<Consumer<ArrayList<GroupTask>>> { - splitSelectStateController.findLastActiveTaskAndRunCallback( - nonPrimaryUserComponent, + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(nonPrimaryUserComponent), taskConsumer ) verify(recentsModel).getTasks(capture()) @@ -283,27 +286,200 @@ class SplitSelectStateControllerTest { tasks.add(groupTask1) // Assertions happen in the callback we get from what we pass into - // #findLastActiveTaskAndRunCallback + // #findLastActiveTasksAndRunCallback + val taskConsumer = + Consumer<List<Task>> { + assertEquals( + "ComponentName package mismatched", + it[0].key.baseIntent.component?.packageName, + matchingPackage + ) + assertEquals( + "ComponentName class mismatched", + it[0].key.baseIntent.component?.className, + matchingClass + ) + assertEquals(it[0], groupTask1.task1) + } + + // Capture callback from recentsModel#getTasks() + val consumer = + withArgCaptor<Consumer<ArrayList<GroupTask>>> { + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(matchingComponent), + taskConsumer + ) + verify(recentsModel).getTasks(capture()) + } + + // Send our mocked tasks + consumer.accept(tasks) + } + + @Test + fun activeTasks_multipleSearchShouldFindTask() { + val nonMatchingComponent = ComponentKey(ComponentName("no", "match"), primaryUserHandle) + val matchingPackage = "hotdog" + val matchingClass = "juice" + val matchingComponent = + ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle) + + val groupTask1 = + generateGroupTask( + ComponentName("hotdog", "pie"), + ComponentName("pumpkin", "pie") + ) + val groupTask2 = + generateGroupTask( + ComponentName("pomegranate", "juice"), + ComponentName(matchingPackage, matchingClass) + ) + val tasks: ArrayList<GroupTask> = ArrayList() + tasks.add(groupTask2) + tasks.add(groupTask1) + + // Assertions happen in the callback we get from what we pass into + // #findLastActiveTasksAndRunCallback + val taskConsumer = + Consumer<List<Task>> { + assertEquals("Expected array length 2", 2, it.size) + assertNull("No tasks should have matched", it[0] /*task*/) + assertEquals( + "ComponentName package mismatched", + it[1].key.baseIntent.component?.packageName, + matchingPackage + ) + assertEquals( + "ComponentName class mismatched", + it[1].key.baseIntent.component?.className, + matchingClass + ) + assertEquals(it[1], groupTask2.task2) + } + + // Capture callback from recentsModel#getTasks() + val consumer = + withArgCaptor<Consumer<ArrayList<GroupTask>>> { + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(nonMatchingComponent, matchingComponent), + taskConsumer + ) + verify(recentsModel).getTasks(capture()) + } + + // Send our mocked tasks + consumer.accept(tasks) + } + + @Test + fun activeTasks_multipleSearchShouldNotFindSameTaskTwice() { + val matchingPackage = "hotdog" + val matchingClass = "juice" + val matchingComponent = + ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle) + + val groupTask1 = + generateGroupTask( + ComponentName("hotdog", "pie"), + ComponentName("pumpkin", "pie") + ) + val groupTask2 = + generateGroupTask( + ComponentName("pomegranate", "juice"), + ComponentName(matchingPackage, matchingClass) + ) + val tasks: ArrayList<GroupTask> = ArrayList() + tasks.add(groupTask2) + tasks.add(groupTask1) + + // Assertions happen in the callback we get from what we pass into + // #findLastActiveTasksAndRunCallback + val taskConsumer = + Consumer<List<Task>> { + assertEquals("Expected array length 2", 2, it.size) + assertEquals( + "ComponentName package mismatched", + it[0].key.baseIntent.component?.packageName, + matchingPackage + ) + assertEquals( + "ComponentName class mismatched", + it[0].key.baseIntent.component?.className, + matchingClass + ) + assertEquals(it[0], groupTask2.task2) + assertNull("No tasks should have matched", it[1] /*task*/) + } + + // Capture callback from recentsModel#getTasks() + val consumer = + withArgCaptor<Consumer<ArrayList<GroupTask>>> { + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(matchingComponent, matchingComponent), + taskConsumer + ) + verify(recentsModel).getTasks(capture()) + } + + // Send our mocked tasks + consumer.accept(tasks) + } + + @Test + fun activeTasks_multipleSearchShouldFindDifferentInstancesOfSameTask() { + val matchingPackage = "hotdog" + val matchingClass = "juice" + val matchingComponent = + ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle) + + val groupTask1 = + generateGroupTask( + ComponentName(matchingPackage, matchingClass), + ComponentName("pumpkin", "pie") + ) + val groupTask2 = + generateGroupTask( + ComponentName("pomegranate", "juice"), + ComponentName(matchingPackage, matchingClass) + ) + val tasks: ArrayList<GroupTask> = ArrayList() + tasks.add(groupTask2) + tasks.add(groupTask1) + + // Assertions happen in the callback we get from what we pass into + // #findLastActiveTasksAndRunCallback val taskConsumer = - Consumer<Task> { + Consumer<List<Task>> { + assertEquals("Expected array length 2", 2, it.size) + assertEquals( + "ComponentName package mismatched", + it[0].key.baseIntent.component?.packageName, + matchingPackage + ) + assertEquals( + "ComponentName class mismatched", + it[0].key.baseIntent.component?.className, + matchingClass + ) + assertEquals(it[0], groupTask1.task1) assertEquals( "ComponentName package mismatched", - it.key.baseIntent.component?.packageName, + it[1].key.baseIntent.component?.packageName, matchingPackage ) assertEquals( "ComponentName class mismatched", - it.key.baseIntent.component?.className, + it[1].key.baseIntent.component?.className, matchingClass ) - assertEquals(it, groupTask2.task2) + assertEquals(it[1], groupTask2.task2) } // Capture callback from recentsModel#getTasks() val consumer = withArgCaptor<Consumer<ArrayList<GroupTask>>> { - splitSelectStateController.findLastActiveTaskAndRunCallback( - matchingComponent, + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(matchingComponent, matchingComponent), taskConsumer ) verify(recentsModel).getTasks(capture()) @@ -366,6 +542,7 @@ class SplitSelectStateControllerTest { ): GroupTask { val task1 = Task() var taskInfo = ActivityManager.RunningTaskInfo() + taskInfo.taskId = getUniqueId() var intent = Intent() intent.component = task1ComponentName taskInfo.baseIntent = intent @@ -373,6 +550,7 @@ class SplitSelectStateControllerTest { val task2 = Task() taskInfo = ActivityManager.RunningTaskInfo() + taskInfo.taskId = getUniqueId() intent = Intent() intent.component = task2ComponentName taskInfo.baseIntent = intent @@ -393,6 +571,7 @@ class SplitSelectStateControllerTest { ): GroupTask { val task1 = Task() var taskInfo = ActivityManager.RunningTaskInfo() + taskInfo.taskId = getUniqueId() // Apply custom userHandle1 taskInfo.userId = userHandle1.identifier var intent = Intent() @@ -401,6 +580,7 @@ class SplitSelectStateControllerTest { task1.key = Task.TaskKey(taskInfo) val task2 = Task() taskInfo = ActivityManager.RunningTaskInfo() + taskInfo.taskId = getUniqueId() // Apply custom userHandle2 taskInfo.userId = userHandle2.identifier intent = Intent() diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java index 83602be72e..a54dc2da18 100644 --- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java +++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java @@ -50,6 +50,9 @@ import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.List; + @SmallTest @RunWith(AndroidJUnit4.class) public class TaskViewSimulatorTest { @@ -150,7 +153,7 @@ public class TaskViewSimulatorTest { WindowBounds wm = new WindowBounds( new Rect(0, 0, mDisplaySize.x, mDisplaySize.y), mDisplayInsets); - WindowBounds[] allBounds = new WindowBounds[4]; + List<WindowBounds> allBounds = new ArrayList<>(4); for (int i = 0; i < 4; i++) { Rect boundsR = new Rect(wm.bounds); Rect insetsR = new Rect(wm.insets); @@ -158,7 +161,7 @@ public class TaskViewSimulatorTest { RotationUtils.rotateRect(insetsR, RotationUtils.deltaRotation(rotation, i)); RotationUtils.rotateRect(boundsR, RotationUtils.deltaRotation(rotation, i)); boundsR.set(0, 0, Math.abs(boundsR.width()), Math.abs(boundsR.height())); - allBounds[i] = new WindowBounds(boundsR, insetsR); + allBounds.add(new WindowBounds(boundsR, insetsR)); } WindowManagerProxy wmProxy = mock(WindowManagerProxy.class); @@ -166,7 +169,7 @@ public class TaskViewSimulatorTest { doReturn(wm).when(wmProxy).getRealBounds(any(), any()); doReturn(NavigationMode.NO_BUTTON).when(wmProxy).getNavigationMode(any()); - ArrayMap<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache = + ArrayMap<CachedDisplayInfo, List<WindowBounds>> perDisplayBoundsCache = new ArrayMap<>(); perDisplayBoundsCache.put(cdi.normalize(), allBounds); diff --git a/res/anim-v33/shared_x_axis_activity_close_enter.xml b/res/anim-v33/shared_x_axis_activity_close_enter.xml index 94ef06c604..3d7ad2bd60 100644 --- a/res/anim-v33/shared_x_axis_activity_close_enter.xml +++ b/res/anim-v33/shared_x_axis_activity_close_enter.xml @@ -25,7 +25,7 @@ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/standard_decelerate" + android:interpolator="@interpolator/standard_decelerate_interpolator" android:startOffset="100" android:duration="350" /> @@ -35,7 +35,7 @@ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/fast_out_extra_slow_in" + android:interpolator="@interpolator/emphasized_interpolator" android:startOffset="0" android:duration="450" /> diff --git a/res/anim-v33/shared_x_axis_activity_close_exit.xml b/res/anim-v33/shared_x_axis_activity_close_exit.xml index 19eb09e4d3..fb63602d4e 100644 --- a/res/anim-v33/shared_x_axis_activity_close_exit.xml +++ b/res/anim-v33/shared_x_axis_activity_close_exit.xml @@ -24,7 +24,7 @@ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/standard_accelerate" + android:interpolator="@interpolator/standard_accelerate_interpolator" android:startOffset="0" android:duration="100" /> @@ -34,7 +34,7 @@ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/fast_out_extra_slow_in" + android:interpolator="@interpolator/emphasized_interpolator" android:startOffset="0" android:duration="450" /> diff --git a/res/anim-v33/shared_x_axis_activity_open_enter.xml b/res/anim-v33/shared_x_axis_activity_open_enter.xml index f699ceca70..cba74ba0ec 100644 --- a/res/anim-v33/shared_x_axis_activity_open_enter.xml +++ b/res/anim-v33/shared_x_axis_activity_open_enter.xml @@ -25,7 +25,7 @@ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/standard_decelerate" + android:interpolator="@interpolator/standard_decelerate_interpolator" android:startOffset="100" android:duration="350" /> @@ -35,7 +35,7 @@ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/fast_out_extra_slow_in" + android:interpolator="@interpolator/emphasized_interpolator" android:startOffset="0" android:duration="450" /> diff --git a/res/anim-v33/shared_x_axis_activity_open_exit.xml b/res/anim-v33/shared_x_axis_activity_open_exit.xml index 85988ecfd2..22e878d7f1 100644 --- a/res/anim-v33/shared_x_axis_activity_open_exit.xml +++ b/res/anim-v33/shared_x_axis_activity_open_exit.xml @@ -24,7 +24,7 @@ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/standard_accelerate" + android:interpolator="@interpolator/standard_accelerate_interpolator" android:startOffset="0" android:duration="100" /> @@ -34,7 +34,7 @@ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/fast_out_extra_slow_in" + android:interpolator="@interpolator/emphasized_interpolator" android:startOffset="0" android:duration="450" /> diff --git a/res/drawable-hdpi/workspace_bg.9.png b/res/drawable-hdpi/workspace_bg.9.png Binary files differdeleted file mode 100755 index 1d82fd41f6..0000000000 --- a/res/drawable-hdpi/workspace_bg.9.png +++ /dev/null diff --git a/res/drawable-mdpi/workspace_bg.9.png b/res/drawable-mdpi/workspace_bg.9.png Binary files differdeleted file mode 100755 index 116ce443f2..0000000000 --- a/res/drawable-mdpi/workspace_bg.9.png +++ /dev/null diff --git a/res/drawable-xhdpi/workspace_bg.9.png b/res/drawable-xhdpi/workspace_bg.9.png Binary files differdeleted file mode 100755 index b1b3b850cc..0000000000 --- a/res/drawable-xhdpi/workspace_bg.9.png +++ /dev/null diff --git a/res/drawable-xxhdpi/workspace_bg.9.png b/res/drawable-xxhdpi/workspace_bg.9.png Binary files differdeleted file mode 100755 index d47f6b2810..0000000000 --- a/res/drawable-xxhdpi/workspace_bg.9.png +++ /dev/null diff --git a/res/drawable-xxxhdpi/workspace_bg.9.png b/res/drawable-xxxhdpi/workspace_bg.9.png Binary files differdeleted file mode 100755 index 328154847e..0000000000 --- a/res/drawable-xxxhdpi/workspace_bg.9.png +++ /dev/null diff --git a/res/drawable/all_apps_tabs_background.xml b/res/drawable/all_apps_tabs_background.xml index 8471cd4539..1e7cff2e93 100644 --- a/res/drawable/all_apps_tabs_background.xml +++ b/res/drawable/all_apps_tabs_background.xml @@ -30,7 +30,7 @@ android:state_selected="false"> <shape android:shape="rectangle"> <corners android:radius="@dimen/all_apps_header_pill_corner_radius" /> - <solid android:color="@color/all_apps_tabs_background" /> + <solid android:color="@color/material_color_surface_bright" /> </shape> </item> @@ -39,7 +39,7 @@ android:state_selected="true"> <shape android:shape="rectangle"> <corners android:radius="@dimen/all_apps_header_pill_corner_radius" /> - <solid android:color="@color/all_apps_tab_background_selected" /> + <solid android:color="@color/material_color_primary" /> </shape> </item> </selector> diff --git a/res/drawable/arrow_toast_rounded_background.xml b/res/drawable/arrow_toast_rounded_background.xml index 1206ddd87b..d7d625567b 100644 --- a/res/drawable/arrow_toast_rounded_background.xml +++ b/res/drawable/arrow_toast_rounded_background.xml @@ -14,6 +14,6 @@ limitations under the License. --> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> - <solid android:color="@color/arrow_tip_view_bg" /> + <solid android:color="?attr/arrowTipBackground" /> <corners android:radius="@dimen/dialogCornerRadius" /> </shape> diff --git a/res/drawable/bubble_ic_overflow_button.xml b/res/drawable/bubble_ic_overflow_button.xml new file mode 100644 index 0000000000..475639e2e2 --- /dev/null +++ b/res/drawable/bubble_ic_overflow_button.xml @@ -0,0 +1,24 @@ +<!-- +Copyright (C) 2023 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="24" + android:viewportHeight="24" + android:width="24dp" + android:height="24dp"> + <path + android:fillColor="#1A73E8" + android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/> +</vector> diff --git a/quickstep/res/drawable/taskbar_divider_bg.xml b/res/drawable/ic_split_exit.xml index 52e230dadd..d7e8b03c50 100644 --- a/quickstep/res/drawable/taskbar_divider_bg.xml +++ b/res/drawable/ic_split_exit.xml @@ -13,8 +13,13 @@ See the License for the specific language governing permissions and limitations under the License. --> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle" > - <solid android:color="@color/taskbar_divider_background"/> - <corners android:radius="1dp" /> -</shape> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="20dp" + android:tint="#000000" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="20dp"> + <path + android:fillColor="@android:color/white" + android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/> +</vector>
\ No newline at end of file diff --git a/res/drawable/ic_wallpaper.xml b/res/drawable/ic_wallpaper.xml deleted file mode 100644 index 9543f88192..0000000000 --- a/res/drawable/ic_wallpaper.xml +++ /dev/null @@ -1,27 +0,0 @@ -<!-- - Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="@dimen/options_menu_icon_size" - android:height="@dimen/options_menu_icon_size" - android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:tint="?android:attr/textColorPrimary"> - <path - android:fillColor="@android:color/white" - android:pathData="M9,12.71l2.14,2.58l3-3.87L18,16.57H6L9,12.71z M5,5h6V3H5C3.9,3,3,3.9,3,5v6h2V5z M19,19h-6v2h6c1.1,0,2-0.9,2-2v-6h-2V19z - M5,19v-6H3v6c0,1.1,0.9,2,2,2h6v-2H5z M19,5v6h2V5c0-1.1-0.9-2-2-2h-6v2H19z M16,9c0.55,0,1-0.45,1-1s-0.45-1-1-1 - c-0.55,0-1,0.45-1,1S15.45,9,16,9z"/> -</vector> diff --git a/res/drawable/bottom_rounded_popup_ripple.xml b/res/drawable/rounded_popup_ripple.xml index 739833a47c..b0dcc80ba2 100644 --- a/res/drawable/bottom_rounded_popup_ripple.xml +++ b/res/drawable/rounded_popup_ripple.xml @@ -18,10 +18,7 @@ <item android:id="@android:id/mask"> <shape android:shape="rectangle"> <solid android:color="#FFFFFFFF"/> - <corners android:bottomLeftRadius="@dimen/dialogCornerRadius" - android:bottomRightRadius="@dimen/dialogCornerRadius" - android:topLeftRadius="0dp" - android:topRightRadius="0dp"/> + <corners android:radius="@dimen/dialogCornerRadius" /> </shape> </item> </ripple>
\ No newline at end of file diff --git a/res/interpolator/back_cancel.xml b/res/interpolator/back_cancel.xml deleted file mode 100644 index 2165457c72..0000000000 --- a/res/interpolator/back_cancel.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2022, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" - android:controlX1="0.2" - android:controlY1="0" - android:controlX2="0" - android:controlY2="1"/>
\ No newline at end of file diff --git a/res/interpolator/folder_interpolator.xml b/res/interpolator/folder_interpolator.xml deleted file mode 100644 index b95d4548ff..0000000000 --- a/res/interpolator/folder_interpolator.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2017, 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. -*/ ---> - -<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" - android:controlX1="0.2" - android:controlY1="0" - android:controlX2="0" - android:controlY2="1"/> diff --git a/res/interpolator/large_folder_preview_item_close_interpolator.xml b/res/interpolator/large_folder_preview_item_close_interpolator.xml deleted file mode 100644 index d28af63f8a..0000000000 --- a/res/interpolator/large_folder_preview_item_close_interpolator.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2017, 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. -*/ ---> - -<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" - android:controlX1="0.3" - android:controlY1="0" - android:controlX2="1" - android:controlY2="1"/> diff --git a/res/layout/all_apps_prediction_row_icon.xml b/res/layout/all_apps_prediction_row_icon.xml new file mode 100644 index 0000000000..c9b327542b --- /dev/null +++ b/res/layout/all_apps_prediction_row_icon.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:launcher="http://schemas.android.com/apk/res-auto" + style="@style/BaseIcon.AllApps" + android:id="@+id/icon" + launcher:iconDisplay="prediction_row" + launcher:centerVertically="true" /> diff --git a/res/layout/app_pair_icon.xml b/res/layout/app_pair_icon.xml new file mode 100644 index 0000000000..2b9a98b037 --- /dev/null +++ b/res/layout/app_pair_icon.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<com.android.launcher3.apppairs.AppPairIcon + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:focusable="true" > + <com.android.launcher3.views.DoubleShadowBubbleTextView + style="@style/BaseIcon.Workspace" + android:id="@+id/app_pair_icon_name" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:focusable="false" + android:layout_gravity="top" /> +</com.android.launcher3.apppairs.AppPairIcon> diff --git a/res/layout/arrow_toast.xml b/res/layout/arrow_toast.xml index 88a92eb565..004e77833a 100644 --- a/res/layout/arrow_toast.xml +++ b/res/layout/arrow_toast.xml @@ -28,7 +28,7 @@ android:padding="16dp" android:background="@drawable/arrow_toast_rounded_background" android:elevation="@dimen/arrow_toast_elevation" - android:textColor="@color/arrow_tip_view_content" + android:textColor="?attr/arrowTipTextColor" android:textSize="14sp"/> <View diff --git a/res/layout/widgets_table_container.xml b/res/layout/widgets_table_container.xml index 4a32672ee7..c41d0bbc90 100644 --- a/res/layout/widgets_table_container.xml +++ b/res/layout/widgets_table_container.xml @@ -16,6 +16,7 @@ <com.android.launcher3.widget.picker.WidgetsListTableView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/widgets_table" + android:paddingHorizontal="@dimen/widget_list_horizontal_margin" android:background="@drawable/bg_widgets_content" android:layout_width="match_parent" android:layout_height="wrap_content" /> diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml index 52c5a492c5..695270e704 100644 --- a/res/layout/work_apps_paused.xml +++ b/res/layout/work_apps_paused.xml @@ -15,7 +15,7 @@ <com.android.launcher3.allapps.WorkPausedCard xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="@dimen/work_edu_card_margin" + android:padding="@dimen/all_apps_tabs_margin_top" android:orientation="vertical" android:gravity="center_horizontal"> @@ -25,7 +25,6 @@ android:id="@+id/work_apps_paused_title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="40dp" android:text="@string/work_apps_paused_title" android:textAlignment="center" android:textSize="18sp" /> diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index ceae5672ab..377025a49c 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Persoonlik"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Werk"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Gesprekke"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Neem notas"</string> <string name="widget_education_header" msgid="4874760613775913787">"Nuttige inligting binne jou bereik"</string> <string name="widget_education_content" msgid="1731667670753497052">"Jy kan legstukke by jou tuisskerm voeg om inligting te kry sonder om programme oop te maak"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tik om legstukinstellings te verander"</string> diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index dbe2acfc10..4638bc3245 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -20,14 +20,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_name" msgid="649227358658669779">"Launcher3"</string> - <string name="work_folder_name" msgid="3753320833950115786">"ሥራ"</string> + <string name="work_folder_name" msgid="3753320833950115786">"ስራ"</string> <string name="activity_not_found" msgid="8071924732094499514">"መተግበሪያ አልተጫነም።"</string> <string name="activity_not_available" msgid="7456344436509528827">"መተግበሪያ አይገኝም"</string> <string name="safemode_shortcut_error" msgid="9160126848219158407">"የወረደው መተግበሪያ ደህንነቱ በተጠበቀ ሁኔታ ውስጥ ተሰናክሏል"</string> <string name="safemode_widget_error" msgid="4863470563535682004">"ምግብሮች በደህንነቱ የተጠበቀ ሁኔታ ተሰናክለዋል"</string> <string name="shortcut_not_available" msgid="2536503539825726397">"አቋራጭ አይገኝም"</string> <string name="home_screen" msgid="5629429142036709174">"መነሻ"</string> - <string name="recent_task_option_split_screen" msgid="6690461455618725183">"የተከፈለ ማያ ገፅ"</string> + <string name="recent_task_option_split_screen" msgid="6690461455618725183">"የተከፈለ ማያ ገጽ"</string> <string name="split_app_info_accessibility" msgid="5475288491241414932">"የመተግበሪያ መረጃ ለ%1$s"</string> <string name="save_app_pair" msgid="5647523853662686243">"የመተግበሪያ ጥምረትን ያስቀምጡ"</string> <string name="long_press_widget_to_add" msgid="3587712543577675817">"ምግብርን ለማንቀሳቀስ ይንኩ እና ይያዙ።"</string> @@ -35,9 +35,9 @@ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string> <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ስፋት በ%2$d ከፍታ"</string> <string name="widget_preview_context_description" msgid="9045841361655787574">"የ<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ምግብር"</string> - <string name="add_item_request_drag_hint" msgid="8730547755622776606">"በመነሻ ማያ ገፅ አካባቢ ላይ ለማንቀሳቀስ ነክተው ይያዙት"</string> - <string name="add_to_home_screen" msgid="9168649446635919791">"ወደ መነሻ ማያ ገፅ አክል"</string> - <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ምግብር ወደ መነሻ ማያ ገፅ ታክሏል"</string> + <string name="add_item_request_drag_hint" msgid="8730547755622776606">"በመነሻ ማያ ገጽ አካባቢ ላይ ለማንቀሳቀስ ነክተው ይያዙት"</string> + <string name="add_to_home_screen" msgid="9168649446635919791">"ወደ መነሻ ማያ ገጽ አክል"</string> + <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ምግብር ወደ መነሻ ማያ ገጽ ታክሏል"</string> <string name="suggested_widgets_header_title" msgid="1844314680798145222">"የአስተያየት ጥቆማዎች"</string> <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ምግብር}one{# ምግብሮች}other{# ምግብሮች}}"</string> <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# አቋራጭ}one{# አቋራጭ}other{# አቋራጮች}}"</string> @@ -48,9 +48,8 @@ <string name="no_widgets_available" msgid="4337693382501046170">"መግብሮች እና አቋራጮች አይገኙም"</string> <string name="no_search_results" msgid="3787956167293097509">"ምንም መግብሮች ወይም አቋራጮች አልተገኙም"</string> <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"የግል"</string> - <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ሥራ"</string> + <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ስራ"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"ውይይቶች"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"የማስታወሻ አያያዝ"</string> <string name="widget_education_header" msgid="4874760613775913787">"በጣቶችዎ ጫፎች ላይ ጠቃሚ መረጃ"</string> <string name="widget_education_content" msgid="1731667670753497052">"መተግበሪያዎችን ሳይከፍቱ መረጃ ለማግኘት በመነሻ ማያ ገጽዎ ላይ ምግብሮችን ማከል ይችላሉ"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"የምግብር ቅንብሮችን ለመለወጥ መታ ያድርጉ"</string> @@ -64,7 +63,7 @@ <string name="notifications_header" msgid="1404149926117359025">"ማሳወቂያዎች"</string> <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"አቋራጭን ለማንቀሳቀስ ይንኩ እና ይያዙ"</string> <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"አቋራጭን ለማንቀሳቀስ ወይም ብጁ እርምጃዎችን ለመጠቀም ሁለቴ መታ ያድርጉ እና ይያዙ።"</string> - <string name="out_of_space" msgid="6455557115204099579">"በዚህ የመነሻ ማያ ገፅ ላይ ምንም ክፍል የለም"</string> + <string name="out_of_space" msgid="6455557115204099579">"በዚህ የመነሻ ማያ ገጽ ላይ ምንም ክፍል የለም"</string> <string name="hotseat_out_of_space" msgid="7448809638125333693">"በተወዳጆች መሣቢያ ውስጥ ተጨማሪ ቦታ የለም"</string> <string name="all_apps_button_label" msgid="8130441508702294465">"የመተግበሪያዎች ዝርዝር"</string> <string name="all_apps_search_results" msgid="5889367432531296759">"የፍለጋ ውጤቶች"</string> @@ -89,9 +88,9 @@ <string name="folder_hint_text" msgid="5174843001373488816">"ስም ያርትዑ"</string> <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ተሰናክሏል"</string> <string name="dotted_app_label" msgid="1865617679843363410">"{count,plural, =1{{app_name}፣ # ማሳወቂያ አለው}one{{app_name}፣ # ማሳወቂያዎች አሉት}other{{app_name}፣ # ማሳወቂያዎች አሉት}}"</string> - <string name="default_scroll_format" msgid="7475544710230993317">"ገፅ %1$d ከ%2$d"</string> - <string name="workspace_scroll_format" msgid="8458889198184077399">"መነሻ ማያ ገፅ %1$d ከ%2$d"</string> - <string name="workspace_new_page" msgid="257366611030256142">"አዲስ የመነሻ ማያ ገፅ"</string> + <string name="default_scroll_format" msgid="7475544710230993317">"ገጽ %1$d ከ%2$d"</string> + <string name="workspace_scroll_format" msgid="8458889198184077399">"መነሻ ማያ ገጽ %1$d ከ%2$d"</string> + <string name="workspace_new_page" msgid="257366611030256142">"አዲስ የመነሻ ማያ ገጽ"</string> <string name="folder_opened" msgid="94695026776264709">"አቃፊ ተከፍቷል፣ <xliff:g id="WIDTH">%1$d</xliff:g> በ<xliff:g id="HEIGHT">%2$d</xliff:g>"</string> <string name="folder_tap_to_close" msgid="4625795376335528256">"አቃፊን ለመዝጋት መታ ያድርጉ"</string> <string name="folder_tap_to_rename" msgid="4017685068016979677">"ዳግም የተሰጠውን ስም ለማስቀመጥ መታ ያድርጉ"</string> @@ -101,10 +100,11 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"አቃፊ፦ <xliff:g id="NAME">%1$s</xliff:g>፣ <xliff:g id="SIZE">%2$d</xliff:g> ወይም ተጨማሪ ንጥሎች"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"የግድግዳ ወረቀቶች"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ልጣፍ እና ቅጥ"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"መነሻ ማያ ገጽን አርትዕ"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"የመነሻ ቅንብሮች"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"በእርስዎ አስተዳዳሪ የተሰናከለ"</string> - <string name="allow_rotation_title" msgid="7222049633713050106">"የመነሻ ማያ ገፅ ማሽከርከርን ይፍቀዱ"</string> + <string name="allow_rotation_title" msgid="7222049633713050106">"የመነሻ ማያ ገጽ ማሽከርከርን ይፍቀዱ"</string> <string name="allow_rotation_desc" msgid="8662546029078692509">"ስልኩ ሲዞር"</string> <string name="notification_dots_title" msgid="9062440428204120317">"የማሳወቂያ ነጥቦች"</string> <string name="notification_dots_desc_on" msgid="1679848116452218908">"አብራ"</string> @@ -130,9 +130,9 @@ <string name="dialog_remove" msgid="6510806469849709407">"አስወግድ"</string> <string name="widgets_list" msgid="796804551140113767">"የመግብሮች ዝርዝር"</string> <string name="widgets_list_closed" msgid="6141506579418771922">"የመግብሮች ዝርዝር ተዘግቷል"</string> - <string name="action_add_to_workspace" msgid="215894119683164916">"ወደ መነሻ ማያ ገፅ አክል"</string> + <string name="action_add_to_workspace" msgid="215894119683164916">"ወደ መነሻ ማያ ገጽ አክል"</string> <string name="action_move_here" msgid="2170188780612570250">"ንጥልን ወደዚህ ውሰድ"</string> - <string name="item_added_to_workspace" msgid="4211073925752213539">"ወደ መነሻ ማያ ገፅ ንጥል ታክሏል"</string> + <string name="item_added_to_workspace" msgid="4211073925752213539">"ወደ መነሻ ማያ ገጽ ንጥል ታክሏል"</string> <string name="item_removed" msgid="851119963877842327">"ንጥል ነገር ተንቀሳቅሷል"</string> <string name="undo" msgid="4151576204245173321">"ቀልብስ"</string> <string name="action_move" msgid="4339390619886385032">"ንጥልን አንቀሳቅስ"</string> @@ -145,7 +145,7 @@ <string name="added_to_folder" msgid="4793259502305558003">"ንጥል ወደ አቃፊ ታክሏል"</string> <string name="create_folder_with" msgid="4050141361160214248">"አቃፊ ፍጠር ከዚህ ጋር፦ <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="folder_created" msgid="6409794597405184510">"አቃፊ ተፈጥሮዋል"</string> - <string name="action_move_to_workspace" msgid="39528912300293768">"ወደ መነሻ ማያ ገፅ አንቀሳቅስ"</string> + <string name="action_move_to_workspace" msgid="39528912300293768">"ወደ መነሻ ማያ ገጽ አንቀሳቅስ"</string> <string name="action_resize" msgid="1802976324781771067">"መጠን ቀይር"</string> <string name="action_increase_width" msgid="8773715375078513326">"ስፋት ጨምር"</string> <string name="action_increase_height" msgid="459390020612501122">"ቁመት ጨምር"</string> diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 90a1e87adf..094fcac7e1 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -47,10 +47,9 @@ <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"محو النص من مربّع البحث"</string> <string name="no_widgets_available" msgid="4337693382501046170">"الأدوات والاختصارات غير متاحة."</string> <string name="no_search_results" msgid="3787956167293097509">"لم يتم العثور على تطبيقات مصغّرة أو اختصارات."</string> - <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"التطبيقات الشخصية"</string> - <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"تطبيقات العمل"</string> + <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"الأدوات الشخصية"</string> + <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"أدوات العمل"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"المحادثات"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"تدوين الملاحظات"</string> <string name="widget_education_header" msgid="4874760613775913787">"معلومات مفيدة في متناول يديك"</string> <string name="widget_education_content" msgid="1731667670753497052">"للحصول على معلومات بدون فتح التطبيقات، يمكنك إضافة التطبيقات المصغّرة إلى الشاشة الرئيسية."</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"انقر لتغيير إعدادات الأداة"</string> @@ -83,7 +82,7 @@ <string name="permlab_write_settings" msgid="4820028712156303762">"تعديل الإعدادات والاختصارات على الشاشة الرئيسية"</string> <string name="permdesc_write_settings" msgid="726859348127868466">"يسمح هذا الإذن للتطبيق بتغيير الإعدادات والاختصارات على الشاشة الرئيسية."</string> <string name="gadget_error_text" msgid="740356548025791839">"يتعذّر تحميل الأداة."</string> - <string name="gadget_setup_text" msgid="8348374825537681407">"إعدادات التطبيق المصغّر"</string> + <string name="gadget_setup_text" msgid="8348374825537681407">"إعدادات الأداة"</string> <string name="gadget_complete_setup_text" msgid="309040266978007925">"انقر لإكمال الإعداد."</string> <string name="uninstall_system_app_text" msgid="4172046090762920660">"هذا تطبيق نظام وتتعذر إزالته."</string> <string name="folder_hint_text" msgid="5174843001373488816">"تعديل الاسم"</string> @@ -101,13 +100,14 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> عنصر أو أكثر"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"الخلفيات"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"الخلفية والأسلوب"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"تعديل الشاشة الرئيسية"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"إعدادات الشاشة الرئيسية"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"أوقف المشرف هذه الميزة"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"السماح بتدوير الشاشة الرئيسية"</string> <string name="allow_rotation_desc" msgid="8662546029078692509">"عند تدوير الهاتف"</string> <string name="notification_dots_title" msgid="9062440428204120317">"نقاط الإشعارات"</string> - <string name="notification_dots_desc_on" msgid="1679848116452218908">"الإعداد مفعّل"</string> + <string name="notification_dots_desc_on" msgid="1679848116452218908">"مفعّلة"</string> <string name="notification_dots_desc_off" msgid="1760796511504341095">"غير مفعّل"</string> <string name="title_missing_notification_access" msgid="7503287056163941064">"يلزم تمكين الوصول إلى الإشعارات"</string> <string name="msg_missing_notification_access" msgid="281113995110910548">"لعرض نقاط الإشعارات، يجب تفعيل إشعارات التطبيق في <xliff:g id="NAME">%1$s</xliff:g>"</string> diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index 587b377b95..583a5c4bbd 100644 --- a/res/values-as/strings.xml +++ b/res/values-as/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ব্যক্তিগত"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"কৰ্মস্থান"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"বাৰ্তালাপ"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"টোকা গ্ৰহণ কৰা"</string> <string name="widget_education_header" msgid="4874760613775913787">"আপোনাৰ আঙুলিৰে টিপতে উপযোগী তথ্য পাওক"</string> <string name="widget_education_content" msgid="1731667670753497052">"এপ্ নোখোলাকৈ তথ্য পাবলৈ আপুনি নিজৰ গৃহ স্ক্ৰীনত ৱিজেট যোগ দিব পাৰে"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ৱিজেটৰ ছেটিং সলনি কৰিবলৈ টিপক"</string> diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index 1e1b63448a..2879158f14 100644 --- a/res/values-az/strings.xml +++ b/res/values-az/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Şəxsi"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"İş"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Söhbətlər"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Qeydgötürmə"</string> <string name="widget_education_header" msgid="4874760613775913787">"Faydalı məlumatlar barmaqlarınızın ucunda"</string> <string name="widget_education_content" msgid="1731667670753497052">"Tətbiqləri açmadan məlumat almaq üçün Əsas ekrana vidcet əlavə edə bilərsiniz"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Vidcet ayarlarını dəyişmək üçün toxunun"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> və ya daha çox element"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Divar kağızları"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Divar kağızı və üslub"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Əsas ekranı redaktə edin"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Home ayarları"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Admininiz tərəfindən deaktiv edilib"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Əsas ekran çevrilsin"</string> diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index 2b3ed53651..be538fe474 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Lično"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Posao"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Konverzacije"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Pravljenje beležaka"</string> <string name="widget_education_header" msgid="4874760613775913787">"Korisne informacije nadohvat ruke"</string> <string name="widget_education_content" msgid="1731667670753497052">"Da biste pronašli informacije bez otvaranja aplikacija, možete da dodate vidžete na početni ekran"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Dodirnite da biste promenili podešavanja vidžeta"</string> @@ -157,8 +156,8 @@ <string name="action_dismiss_notification" msgid="5909461085055959187">"Odbaci"</string> <string name="accessibility_close" msgid="2277148124685870734">"Zatvori"</string> <string name="notification_dismissed" msgid="6002233469409822874">"Obaveštenje je odbačeno"</string> - <string name="all_apps_personal_tab" msgid="4190252696685155002">"Lično"</string> - <string name="all_apps_work_tab" msgid="4884822796154055118">"Posao"</string> + <string name="all_apps_personal_tab" msgid="4190252696685155002">"Lične"</string> + <string name="all_apps_work_tab" msgid="4884822796154055118">"Poslovne"</string> <string name="work_profile_toggle_label" msgid="3081029915775481146">"Poslovni profil"</string> <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Poslovne aplikacije su označene značkom i IT administrator može da ih vidi"</string> <string name="work_profile_edu_accept" msgid="6069788082535149071">"Važi"</string> @@ -169,7 +168,7 @@ <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Poslovne aplikacije su označene značkom i IT administrator može da ih vidi"</string> <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Važi"</string> <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pauziraj poslovne aplikacije"</string> - <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Ponovo aktiviraj"</string> + <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Opozovi pauzu"</string> <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string> <string name="search_pref_screen_title" msgid="3258959643336315962">"Pretražite telefon"</string> <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Pretražite tablet"</string> diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index 1eee242194..7bdd1ec50c 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Асабістыя"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Працоўныя"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Размовы"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Стварэнне нататак"</string> <string name="widget_education_header" msgid="4874760613775913787">"Карысная інфармацыя ў вас пад рукой"</string> <string name="widget_education_content" msgid="1731667670753497052">"Каб не адкрываць праграмы для прагляду патрэбнай інфармацыі, дадайце віджэты на галоўны экран"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Націсніце, каб змяніць налады віджэта"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, элементы: <xliff:g id="SIZE">%2$d</xliff:g> ці больш"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Шпалеры"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Шпалеры і стыль"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Змяніць Галоўны экран"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Налады галоўнага экрана"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Адключаная адміністратарам"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Дазволіць паварот галоўнага экрана"</string> diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index f59dcfbca4..992abeb214 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Лични"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Служебни"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Разговори"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Водене на бележки"</string> <string name="widget_education_header" msgid="4874760613775913787">"Лесен достъп до полезна информация"</string> <string name="widget_education_content" msgid="1731667670753497052">"За да получавате информация, без да отваряте приложенията, можете да добавите приспособления към началния екран"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Докоснете, за да промените настройките на приспособлението"</string> diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index 326fc7b1c3..995166fb36 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ব্যক্তিগত"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"অফিস"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"কথোপকথন"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"নোট নেওয়া"</string> <string name="widget_education_header" msgid="4874760613775913787">"সহজেই দরকারি তথ্য পান"</string> <string name="widget_education_content" msgid="1731667670753497052">"অ্যাপ না খুলেই তথ্য পাওয়ার জন্য, হোম স্ক্রিনে উইজেট যোগ করতে পারবেন"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"উইজেট সেটিংস পরিবর্তন করতে ট্যাপ করুন"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g>টি বা তার বেশি আইটেম"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"ওয়ালপেপারগুলি"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ওয়ালপেপার এবং স্টাইল"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"হোম স্ক্রিন এডিট করুন"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"হোম সেটিংস"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"আপনার প্রশাসক দ্বারা অক্ষম করা হয়েছে"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"হোম স্ক্রিন রোটেট করার অনুমতি দিন"</string> @@ -160,7 +160,7 @@ <string name="all_apps_personal_tab" msgid="4190252696685155002">"ব্যক্তিগত"</string> <string name="all_apps_work_tab" msgid="4884822796154055118">"অফিস"</string> <string name="work_profile_toggle_label" msgid="3081029915775481146">"অফিসের প্রোফাইল"</string> - <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"অফিস অ্যাপে ব্যাজ যোগ করা হয়েছে এবং আপনার আইটি অ্যাডমিন সেগুলি দেখতে পাবেন"</string> + <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"অফিসের অ্যাপে ব্যাজ যোগ করা হয়েছে এবং আপনার আইটি অ্যাডমিন সেগুলি দেখতে পাবেন"</string> <string name="work_profile_edu_accept" msgid="6069788082535149071">"বুঝেছি"</string> <string name="work_apps_paused_title" msgid="3040901117349444598">"অফিস অ্যাপ বন্ধ করা আছে"</string> <string name="work_apps_paused_info_body" msgid="1687828929959237477">"আপনার অফিসের অ্যাপ থেকে আপনি কোনও বিজ্ঞপ্তি পাবেন না"</string> diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 8c3dd0862c..20fc18a6cc 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Lično"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Posao"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Razgovori"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Pisanje bilješki"</string> <string name="widget_education_header" msgid="4874760613775913787">"Korisne informacije nadohvat ruke"</string> <string name="widget_education_content" msgid="1731667670753497052">"Da dobijete informacije bez otvaranja aplikacija, možete dodati vidžete na početni ekran"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Dodirnite da promijenite postavke vidžeta"</string> @@ -169,7 +168,7 @@ <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Poslovne aplikacije su označene i vaš IT administrator ih može vidjeti"</string> <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Razumijem"</string> <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pauziraj poslovne aplikacije"</string> - <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Ponovo pokreni"</string> + <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Prekini pauzu"</string> <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrirajte"</string> <string name="search_pref_screen_title" msgid="3258959643336315962">"Pretražite telefon"</string> <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Pretražite tablet"</string> diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 9fe81dd6a6..7d45271cea 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Treball"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Converses"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Presa de notes"</string> <string name="widget_education_header" msgid="4874760613775913787">"Informació útil a l\'abast de la mà"</string> <string name="widget_education_content" msgid="1731667670753497052">"Per obtenir informació sense obrir les aplicacions, pots afegir widgets a la pantalla d\'inici"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toca per canviar la configuració del widget"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o més elements"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Fons de pantalla"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Estil i fons de pantalla"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Edita la pantalla d\'inici"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Config. pantalla d\'inici"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desactivada per l\'administrador"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Permet la rotació de la pantalla d\'inici"</string> diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 40e1397715..ac093e45f1 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -27,7 +27,7 @@ <string name="safemode_widget_error" msgid="4863470563535682004">"V nouzovém režimu jsou widgety zakázány."</string> <string name="shortcut_not_available" msgid="2536503539825726397">"Zkratka není k dispozici"</string> <string name="home_screen" msgid="5629429142036709174">"Domů"</string> - <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Rozdělit obrazovku"</string> + <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Rozdělená obrazovka"</string> <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informace o aplikaci %1$s"</string> <string name="save_app_pair" msgid="5647523853662686243">"Uložit pár aplikací"</string> <string name="long_press_widget_to_add" msgid="3587712543577675817">"Widget přesunete klepnutím a podržením."</string> @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Osobní"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Práce"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Konverzace"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Psaní poznámek"</string> <string name="widget_education_header" msgid="4874760613775913787">"Užitečné informace na dosah"</string> <string name="widget_education_content" msgid="1731667670753497052">"Pokud chcete mít informace k dispozici bez otevírání aplikací, můžete si na plochu přidat widgety"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Klepnutím změníte nastavení widgetu"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Složka: <xliff:g id="NAME">%1$s</xliff:g>, počet položek: <xliff:g id="SIZE">%2$d</xliff:g> nebo více"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Tapeta a styl"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Upravit plochu"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Nastavení plochy"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázáno administrátorem"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Povolit otáčení plochy"</string> @@ -172,6 +172,6 @@ <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Zrušit pozastavení"</string> <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtr"</string> <string name="search_pref_screen_title" msgid="3258959643336315962">"Prohledávání telefonu"</string> - <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Vyhledávání na tabletu"</string> + <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Vyhledávat na tabletu"</string> <string name="remote_action_failed" msgid="1383965239183576790">"Selhalo: <xliff:g id="WHAT">%1$s</xliff:g>"</string> </resources> diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 5867716cbb..66a191a34d 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -47,10 +47,9 @@ <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Ryd teksten i søgefeltet"</string> <string name="no_widgets_available" msgid="4337693382501046170">"Der er ingen tilgængelige widgets eller genveje"</string> <string name="no_search_results" msgid="3787956167293097509">"Der blev ikke fundet nogen widgets eller genveje"</string> - <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personlig"</string> + <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personlige"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Arbejde"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Samtaler"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Notetagning"</string> <string name="widget_education_header" msgid="4874760613775913787">"Nyttige oplysninger lige ved hånden"</string> <string name="widget_education_content" msgid="1731667670753497052">"Hvis du vil have oplysninger uden at åbne apps, kan du føje widgets til din startskærm"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tryk for at ændre widgetindstillinger"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller flere elementer"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Baggrunde"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Baggrund og stil"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Rediger startskærm"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Indst. for startskærm"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Deaktiveret af din administrator"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Tillad rotation af startskærmen"</string> diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 93ea28e2c3..959840ddec 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -27,7 +27,7 @@ <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets im abgesicherten Modus deaktiviert"</string> <string name="shortcut_not_available" msgid="2536503539825726397">"Verknüpfung nicht verfügbar"</string> <string name="home_screen" msgid="5629429142036709174">"Startbildschirm"</string> - <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Splitscreen"</string> + <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Geteilter Bildschirm"</string> <string name="split_app_info_accessibility" msgid="5475288491241414932">"App-Info für %1$s"</string> <string name="save_app_pair" msgid="5647523853662686243">"App-Paar speichern"</string> <string name="long_press_widget_to_add" msgid="3587712543577675817">"Zum Verschieben des Widgets berühren und halten"</string> @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Privat"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Geschäftlich"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Unterhaltungen"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Notizen"</string> <string name="widget_education_header" msgid="4874760613775913787">"Praktische Informationen – immer zur Hand"</string> <string name="widget_education_content" msgid="1731667670753497052">"Wenn du Informationen erhalten möchtest, ohne Apps zu öffnen, kannst du deinem Startbildschirm Widgets hinzufügen"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tippen, um die Widget-Einstellungen zu ändern"</string> @@ -100,8 +99,9 @@ <string name="folder_name_format_exact" msgid="8626242716117004803">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> Elemente"</string> <string name="folder_name_format_overflow" msgid="4270108890534995199">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> oder mehr Elemente"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Hintergründe"</string> - <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Hintergrund und Stil"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Startbildschirm bearbeiten"</string> + <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Hintergrund & Stil"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Einstellungen"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Von deinem Administrator deaktiviert"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Drehen des Startbildschirms zulassen"</string> diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 6b6f249407..b517975162 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Προσωπικά"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Εργασίας"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Συζητήσεις"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Δημιουργία σημειώσεων"</string> <string name="widget_education_header" msgid="4874760613775913787">"Χρήσιμες πληροφορίες στη διάθεσή σας"</string> <string name="widget_education_content" msgid="1731667670753497052">"Για να λάβετε πληροφορίες χωρίς να ανοίξετε εφαρμογές, μπορείτε να προσθέσετε γραφικά στοιχεία στην αρχική οθόνη."</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Πατήστε για αλλαγή των ρυθμίσεων του γραφικού στοιχείου"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Φάκελος: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ή περισσότερα στοιχεία"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Ταπετσαρίες"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Ταπετσαρία και στιλ"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Επεξεργασία αρχικής οθόνης"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Ρυθμίσεις Αρχ. Οθ."</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Απενεργοποιήθηκε από τον διαχειριστή σας"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Να επιτρέπεται η περιστροφή της αρχικής οθόνης"</string> diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index aa926df6d4..eea6581f36 100644 --- a/res/values-en-rAU/strings.xml +++ b/res/values-en-rAU/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Work"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Note-taking"</string> <string name="widget_education_header" msgid="4874760613775913787">"Useful info at your fingertips"</string> <string name="widget_education_content" msgid="1731667670753497052">"To get info without opening apps, you can add widgets to your home screen"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string> diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml index 40534ca74e..b00fee6e35 100644 --- a/res/values-en-rCA/strings.xml +++ b/res/values-en-rCA/strings.xml @@ -30,12 +30,12 @@ <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string> <string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string> <string name="save_app_pair" msgid="5647523853662686243">"Save app pair"</string> - <string name="long_press_widget_to_add" msgid="3587712543577675817">"Touch and hold to move a widget."</string> - <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Double-tap and hold to move a widget or use custom actions."</string> + <string name="long_press_widget_to_add" msgid="3587712543577675817">"Touch & hold to move a widget."</string> + <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Double-tap & hold to move a widget or use custom actions."</string> <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string> <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string> <string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string> - <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Touch and hold the widget to move it around the home screen"</string> + <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Touch & hold the widget to move it around the home screen"</string> <string name="add_to_home_screen" msgid="9168649446635919791">"Add to home screen"</string> <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to home screen"</string> <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string> @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Work"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Note-taking"</string> <string name="widget_education_header" msgid="4874760613775913787">"Useful info at your fingertips"</string> <string name="widget_education_content" msgid="1731667670753497052">"To get info without opening apps, you can add widgets to your home screen"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string> @@ -62,8 +61,8 @@ <string name="label_application" msgid="8531721983832654978">"App"</string> <string name="all_apps_label" msgid="5015784846527570951">"All apps"</string> <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string> - <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Touch and hold to move a shortcut."</string> - <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Double-tap and hold to move a shortcut or use custom actions."</string> + <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Touch & hold to move a shortcut."</string> + <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Double-tap & hold to move a shortcut or use custom actions."</string> <string name="out_of_space" msgid="6455557115204099579">"No room on this home screen"</string> <string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favorites tray"</string> <string name="all_apps_button_label" msgid="8130441508702294465">"Apps list"</string> @@ -100,7 +99,7 @@ <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string> <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string> - <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper and style"</string> + <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper & style"</string> <string name="edit_home_screen" msgid="8947858375782098427">"Edit Home Screen"</string> <string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string> diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index aa926df6d4..eea6581f36 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Work"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Note-taking"</string> <string name="widget_education_header" msgid="4874760613775913787">"Useful info at your fingertips"</string> <string name="widget_education_content" msgid="1731667670753497052">"To get info without opening apps, you can add widgets to your home screen"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string> diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index aa926df6d4..eea6581f36 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Work"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Note-taking"</string> <string name="widget_education_header" msgid="4874760613775913787">"Useful info at your fingertips"</string> <string name="widget_education_content" msgid="1731667670753497052">"To get info without opening apps, you can add widgets to your home screen"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string> diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml index d7a5d95adc..31fea4b7be 100644 --- a/res/values-en-rXC/strings.xml +++ b/res/values-en-rXC/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Work"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Note-taking"</string> <string name="widget_education_header" msgid="4874760613775913787">"Useful info at your fingertips"</string> <string name="widget_education_content" msgid="1731667670753497052">"To get info without opening apps, you can add widgets to your home screen"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string> diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index bffa3f81f9..35b6124b23 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personales"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabajo"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversaciones"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Tomar notas"</string> <string name="widget_education_header" msgid="4874760613775913787">"Información útil a tu alcance"</string> <string name="widget_education_content" msgid="1731667670753497052">"Para recibir información de apps sin abrirlas, puedes agregar widgets a la pantalla principal"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Presiona para cambiar la configuración del widget"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o más elementos"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fondo de pantalla y estilo"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Editar pantalla principal"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Configuración de pantalla principal"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"El administrador inhabilitó esta función"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Permitir la rotación de la pantalla principal"</string> @@ -172,6 +172,6 @@ <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Reanudar"</string> <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtro"</string> <string name="search_pref_screen_title" msgid="3258959643336315962">"Busca tu teléfono"</string> - <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Busca en tu tablet"</string> + <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Busca tu tablet"</string> <string name="remote_action_failed" msgid="1383965239183576790">"Error: <xliff:g id="WHAT">%1$s</xliff:g>"</string> </resources> diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 2ebefc0491..323feb0c70 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personales"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabajo"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversaciones"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Toma de notas"</string> <string name="widget_education_header" msgid="4874760613775913787">"Información útil al alcance de la mano"</string> <string name="widget_education_content" msgid="1731667670753497052">"Para ver información sin abrir una aplicación, puedes añadir widgets a la pantalla de inicio"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toca para cambiar los ajustes del widget"</string> @@ -106,7 +105,7 @@ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inhabilitado por el administrador"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Permitir rotación de la pantalla de inicio"</string> <string name="allow_rotation_desc" msgid="8662546029078692509">"Al girar el teléfono"</string> - <string name="notification_dots_title" msgid="9062440428204120317">"Burbujas de notificación"</string> + <string name="notification_dots_title" msgid="9062440428204120317">"Puntos de notificación"</string> <string name="notification_dots_desc_on" msgid="1679848116452218908">"Activado"</string> <string name="notification_dots_desc_off" msgid="1760796511504341095">"Desactivadas"</string> <string name="title_missing_notification_access" msgid="7503287056163941064">"Se necesita acceso a las notificaciones"</string> @@ -172,6 +171,6 @@ <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Reanudar"</string> <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtro"</string> <string name="search_pref_screen_title" msgid="3258959643336315962">"Busca en tu teléfono"</string> - <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Buscar en tu tablet"</string> + <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Busca en tu tablet"</string> <string name="remote_action_failed" msgid="1383965239183576790">"Se ha producido un error: <xliff:g id="WHAT">%1$s</xliff:g>"</string> </resources> diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index 4b00f7bd71..cf6e1c4c45 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Isiklikud"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Töö"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Vestlused"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Märkmete tegemine"</string> <string name="widget_education_header" msgid="4874760613775913787">"Kasulik teave on teie käeulatuses"</string> <string name="widget_education_content" msgid="1731667670753497052">"Teabe saamiseks rakendusi avamata võite oma avakuvale lisada vidinaid"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Puudutage vidina seadete muutmiseks"</string> @@ -72,7 +71,7 @@ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Töörakenduste loend"</string> <string name="remove_drop_target_label" msgid="7812859488053230776">"Eemalda"</string> <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalli"</string> - <string name="app_info_drop_target_label" msgid="692894985365717661">"Rakenduse teave"</string> + <string name="app_info_drop_target_label" msgid="692894985365717661">"Rakenduste teave"</string> <string name="install_drop_target_label" msgid="2539096853673231757">"Installimine"</string> <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ära soovita rakendust"</string> <string name="pin_prediction" msgid="4196423321649756498">"Kinnita ennustus"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> või rohkem üksust"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustapildid"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Taustapilt ja stiil"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Muuda avaekraani"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Avakuva seaded"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Keelas administraator"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Luba avakuva pööramine"</string> @@ -169,7 +169,7 @@ <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Töörakendustel on märk ja need on teie IT-administraatorile nähtavad"</string> <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Selge"</string> <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Peata töörakendused"</string> - <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Lõpeta peatamine"</string> + <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Jätka"</string> <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string> <string name="search_pref_screen_title" msgid="3258959643336315962">"Otsimine telefonist"</string> <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Otsimine tahvelarvutist"</string> diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index 4d617c5bb7..88f3a95ca6 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Pertsonalak"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Lanekoak"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Elkarrizketak"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Oharrak idazteko"</string> <string name="widget_education_header" msgid="4874760613775913787">"Informazio erabilgarria beti eskura"</string> <string name="widget_education_content" msgid="1731667670753497052">"Aplikaziorik ireki beharrik gabe informazioa zuzenean jasotzeko, gehitu widgetak hasierako pantailan"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Sakatu hau widgeten ezarpenak aldatzeko"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"<xliff:g id="NAME">%1$s</xliff:g> karpeta (<xliff:g id="SIZE">%2$d</xliff:g> elementu edo gehiago)"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Horma-paperak"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Horma-papera eta estiloa"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Editatu hasierako pantaila"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Hasierako pantailaren ezarpenak"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratzaileak desgaitu du"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Eman hasierako pantaila biratzeko baimena"</string> @@ -162,7 +162,7 @@ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Laneko profila"</string> <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Laneko aplikazioek bereizgarriak dituzte, eta IKT saileko administratzaileak ikus ditzake"</string> <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ados"</string> - <string name="work_apps_paused_title" msgid="3040901117349444598">"Pausatuta daude laneko aplikazioak"</string> + <string name="work_apps_paused_title" msgid="3040901117349444598">"Pausatu egin dira laneko aplikazioak"</string> <string name="work_apps_paused_info_body" msgid="1687828929959237477">"Ez duzu jasoko laneko aplikazioen jakinarazpenik"</string> <string name="work_apps_paused_body" msgid="261634750995824906">"Laneko aplikazioek ezin dute jakinarazpenik bidali, bateria erabili edo kokapena atzitu"</string> <string name="work_apps_paused_telephony_unavailable_body" msgid="8358872357502756790">"Ez duzu jasoko laneko aplikazioen telefono-deirik, testu-mezurik edo jakinarazpenik"</string> diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 091211adc6..6708519c14 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ابزارکهای شخصی"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"کار"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"مکالمهها"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"یادداشتبرداری"</string> <string name="widget_education_header" msgid="4874760613775913787">"دسترسی آسان به اطلاعات سودمند"</string> <string name="widget_education_content" msgid="1731667670753497052">"با افزودن ابزارکها به صفحه اصلی میتوانید اطلاعات را بدون باز کردن برنامهها دریافت کنید"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"برای تغییر تنظیمات ابزارک، ضربه بزنید"</string> diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index a1aa48c169..df7b6ac222 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Henkilökohtaiset"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Työ"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Keskustelut"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Muistiinpanojen tekeminen"</string> <string name="widget_education_header" msgid="4874760613775913787">"Hyödyllisiä tietoja käden ulottuvilla"</string> <string name="widget_education_content" msgid="1731667670753497052">"Jos haluat nähdä tietoja avaamatta sovelluksia, voit lisätä aloitusnäytölle widgetejä"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Napauta, niin voit muuttaa widgetin asetuksia"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Kansio: <xliff:g id="NAME">%1$s</xliff:g>, ainakin <xliff:g id="SIZE">%2$d</xliff:g> kohdetta"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustakuvat"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Taustakuva ja tyyli"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Muokkaa aloitusnäyttöä"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Aloitusnäyttö"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Järjestelmänvalvoja on poistanut toiminnon käytöstä."</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Salli aloitusnäytön kiertäminen"</string> diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 74789684f3..6adcb61185 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personnels"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Professionnels"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Prise de note"</string> <string name="widget_education_header" msgid="4874760613775913787">"Renseignements utiles à portée de main"</string> <string name="widget_education_content" msgid="1731667670753497052">"Pour obtenir des informations sans ouvrir d\'applications, vous pouvez ajouter des widgets à votre écran d\'accueil"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Touchez pour modifier les paramètres du widget"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> éléments ou plus"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fond d\'écran et style"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Modifier l\'écran d\'accueil"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Paramètres d\'accueil"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Cette fonction est désactivée par votre administrateur"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Autoriser la rotation de l\'écran d\'accueil"</string> diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 63869f654a..8b546281ec 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personnels"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Professionnels"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Prise de notes"</string> <string name="widget_education_header" msgid="4874760613775913787">"Infos utiles à portée de main"</string> <string name="widget_education_content" msgid="1731667670753497052">"Pour obtenir des infos sans ouvrir d\'applis, vous pouvez ajouter des widgets à votre écran d\'accueil"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Appuyez pour modifier les paramètres du widget"</string> @@ -101,14 +100,15 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> éléments ou plus"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fond d\'écran et style"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Modifier l\'écran d\'accueil"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Paramètres de l\'accueil"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Désactivé par votre administrateur"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Autoriser la rotation de l\'écran d\'accueil"</string> <string name="allow_rotation_desc" msgid="8662546029078692509">"Lorsque vous faites pivoter le téléphone"</string> <string name="notification_dots_title" msgid="9062440428204120317">"Pastilles de notification"</string> <string name="notification_dots_desc_on" msgid="1679848116452218908">"Activé"</string> - <string name="notification_dots_desc_off" msgid="1760796511504341095">"Désactivé"</string> + <string name="notification_dots_desc_off" msgid="1760796511504341095">"Désactivées"</string> <string name="title_missing_notification_access" msgid="7503287056163941064">"Accès aux notifications requis"</string> <string name="msg_missing_notification_access" msgid="281113995110910548">"Pour afficher les pastilles de notification, activez les notifications de l\'application <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="title_change_settings" msgid="1376365968844349552">"Modifier les paramètres"</string> @@ -162,7 +162,7 @@ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil professionnel"</string> <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Les applis professionnelles sont identifiées par un badge et votre administrateur informatique peut les voir"</string> <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string> - <string name="work_apps_paused_title" msgid="3040901117349444598">"Applis professionnelles en pause"</string> + <string name="work_apps_paused_title" msgid="3040901117349444598">"Applications professionnelles en veille"</string> <string name="work_apps_paused_info_body" msgid="1687828929959237477">"Vous ne recevrez pas de notifications de vos applications professionnelles"</string> <string name="work_apps_paused_body" msgid="261634750995824906">"Vos applis professionnelles ne peuvent pas vous envoyer de notifications, utiliser votre batterie ni accéder à votre position"</string> <string name="work_apps_paused_telephony_unavailable_body" msgid="8358872357502756790">"Vous ne recevrez pas d\'appels téléphoniques, de messages ni de notifications de vos applications professionnelles"</string> diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index ab68e22be7..c1f4f3c636 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Widgets persoais"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Widgets do traballo"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversas"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Toma de notas"</string> <string name="widget_education_header" msgid="4874760613775913787">"Información útil ao teu alcance"</string> <string name="widget_education_content" msgid="1731667670753497052">"Se queres obter información sen abrir as aplicacións, podes engadir widgets á pantalla de inicio"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toca para cambiar a configuración do widget"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementos ou máis"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Estilo e fondo de pantalla"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Editar pantalla de inicio"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Axustes de Inicio"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Función desactivada polo administrador"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Permitir xirar a pantalla de inicio"</string> diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index 80aaba3be1..a804870ddb 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -50,13 +50,12 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"વ્યક્તિગત"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ઑફિસ"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"વાતચીતો"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"નોંધ લેવી"</string> <string name="widget_education_header" msgid="4874760613775913787">"ઉપયોગી માહિતી તમારી આંગળીના ટેરવે"</string> <string name="widget_education_content" msgid="1731667670753497052">"ઍપને ખોલ્યા વિના માહિતી મેળવવા માટે, તમે તમારી હોમ સ્ક્રીનમાં વિજેટ ઉમેરી શકો છો"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"વિજેટના સેટિંગ બદલવા માટે ટૅપ કરો"</string> <string name="widget_education_close_button" msgid="8676165703104836580">"સમજાઈ ગયું"</string> <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"વિજેટના સેટિંગ બદલો"</string> - <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ઍપ શોધો"</string> + <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"શોધ ઍપ્લિકેશનો"</string> <string name="all_apps_loading_message" msgid="5813968043155271636">"ઍપ્લિકેશનો લોડ કરી રહ્યું છે…"</string> <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી"</string> <string name="label_application" msgid="8531721983832654978">"ઍપ"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> કે વધુ આઇટમ"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"વૉલપેપર"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"વૉલપેપર અને સ્ટાઇલ"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"હોમ સ્ક્રીનમાં ફેરફાર કરો"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"હોમ સેટિંગ"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"તમારા વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"હોમ સ્ક્રીનને ફેરવવાની મંજૂરી આપો"</string> @@ -157,7 +157,7 @@ <string name="action_dismiss_notification" msgid="5909461085055959187">"છોડી દો"</string> <string name="accessibility_close" msgid="2277148124685870734">"બંધ કરો"</string> <string name="notification_dismissed" msgid="6002233469409822874">"સૂચના છોડી દીધી"</string> - <string name="all_apps_personal_tab" msgid="4190252696685155002">"વ્યક્તિગત ઍપ"</string> + <string name="all_apps_personal_tab" msgid="4190252696685155002">"મનગમતી ઍપ"</string> <string name="all_apps_work_tab" msgid="4884822796154055118">"ઑફિસની ઍપ"</string> <string name="work_profile_toggle_label" msgid="3081029915775481146">"ઑફિસની પ્રોફાઇલ"</string> <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ઑફિસની ઍપને બૅજ આપેલા હોય છે અને તમારા IT ઍડમિન તેમને જોઈ શકે છે"</string> diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 8e7e835502..ce83230be1 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -48,15 +48,14 @@ <string name="no_widgets_available" msgid="4337693382501046170">"विजेट और शॉर्टकट उपलब्ध नहीं हैं"</string> <string name="no_search_results" msgid="3787956167293097509">"कोई विजेट या शॉर्टकट नहीं मिला"</string> <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"निजी विजेट"</string> - <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"वर्क विजेट"</string> + <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ऑफ़िस"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"बातचीत"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"नोट बनाने से जुड़े विजेट"</string> <string name="widget_education_header" msgid="4874760613775913787">"काम की जानकारी आसानी से पाएं"</string> <string name="widget_education_content" msgid="1731667670753497052">"ऐप्लिकेशन को खोले बिना उनकी जानकारी पाने के लिए, होम स्क्रीन पर विजेट जोड़े जा सकते हैं"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"विजेट की सेटिंग में बदलाव करने के लिए टैप करें"</string> <string name="widget_education_close_button" msgid="8676165703104836580">"ठीक है"</string> <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"विजेट की सेटिंग में बदलाव करें"</string> - <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ऐप्लिकेशन खोजें"</string> + <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ऐप सर्च करें"</string> <string name="all_apps_loading_message" msgid="5813968043155271636">"ऐप्लिकेशन लोड हो रहे हैं…"</string> <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" से मिलता-जुलता कोई ऐप्लिकेशन नहीं मिला"</string> <string name="label_application" msgid="8531721983832654978">"ऐप्लिकेशन"</string> @@ -101,8 +100,9 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"फ़ोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> या इससे ज़्यादा आइटम"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"वॉलपेपर और स्टाइल"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"होम स्क्रीन में बदलाव करें"</string> - <string name="settings_button_text" msgid="8873672322605444408">"होम स्क्रीन की सेटिंग"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> + <string name="settings_button_text" msgid="8873672322605444408">"होम पेज की सेटिंग"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपके एडमिन ने बंद किया हुआ है"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"होम स्क्रीन घुमाने की अनुमति दें"</string> <string name="allow_rotation_desc" msgid="8662546029078692509">"फ़ोन घुुमाए जाने पर"</string> @@ -160,7 +160,7 @@ <string name="all_apps_personal_tab" msgid="4190252696685155002">"निजी ऐप्लिकेशन"</string> <string name="all_apps_work_tab" msgid="4884822796154055118">"वर्क ऐप्लिकेशन"</string> <string name="work_profile_toggle_label" msgid="3081029915775481146">"वर्क प्रोफ़ाइल"</string> - <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"वर्क ऐप्लिकेशन बैज किए गए हैं. आईटी एडमिन इन्हें देख सकता है"</string> + <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"वर्क ऐप्लिकेशन बैज किए गए हैं और आईटी एडमिन को दिख रहे हैं"</string> <string name="work_profile_edu_accept" msgid="6069788082535149071">"ठीक है"</string> <string name="work_apps_paused_title" msgid="3040901117349444598">"वर्क ऐप्लिकेशन रोके गए"</string> <string name="work_apps_paused_info_body" msgid="1687828929959237477">"आपको वर्क ऐप्लिकेशन से सूचनाएं नहीं मिलेंगी"</string> diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 3e52b18f2f..73e366f04c 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Osobni"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Posao"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Razgovori"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Pisanje bilježaka"</string> <string name="widget_education_header" msgid="4874760613775913787">"Korisne informacije nadohvat ruke"</string> <string name="widget_education_content" msgid="1731667670753497052">"Da biste dobili informacije bez otvaranja aplikacija, možete dodati widgete na početni zaslon"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Dodirnite da biste promijenili postavke widgeta"</string> diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 979539d8bb..338047620d 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Személyes"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Munka"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Beszélgetések"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Jegyzetelés"</string> <string name="widget_education_header" msgid="4874760613775913787">"Hasznos információk egy koppintásnyira"</string> <string name="widget_education_content" msgid="1731667670753497052">"Ha az alkalmazások megnyitása nélkül szeretne információhoz jutni, felvehet modulokat a kezdőképernyőre"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Ide koppintva módosíthatja a modulbeállításokat"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> vagy több elem"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Háttérképek"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Háttérkép és stílus"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Kezdőképernyő szerkesztése"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Kezdőképernyő beállításai"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"A rendszergazda letiltotta"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"A kezdőképernyő elforgatásának engedélyezése"</string> @@ -160,13 +160,13 @@ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Személyes"</string> <string name="all_apps_work_tab" msgid="4884822796154055118">"Munkahelyi"</string> <string name="work_profile_toggle_label" msgid="3081029915775481146">"Munkaprofil"</string> - <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"A munkahelyi alkalmazások jelvénnyel vannak megjelölve, és ezeket láthatja a rendszergazda"</string> + <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"A munkahelyi alkalmazások jelvénnyel vannak megjelölve, és láthatók a rendszergazda számára"</string> <string name="work_profile_edu_accept" msgid="6069788082535149071">"Értem"</string> <string name="work_apps_paused_title" msgid="3040901117349444598">"A munkahelyi alkalmazások szüneteltetve vannak"</string> <string name="work_apps_paused_info_body" msgid="1687828929959237477">"A munkahelyi alkalmazásoktól nem kap értesítést."</string> <string name="work_apps_paused_body" msgid="261634750995824906">"A munkahelyi alkalmazások nem küldhetnek Önnek értesítéseket, nem használhatják az akkumulátorát, és nem férhetnek hozzá a tartózkodási helyéhez."</string> <string name="work_apps_paused_telephony_unavailable_body" msgid="8358872357502756790">"A munkahelyi alkalmazásoktól nem kap telefonhívásokat, szöveges üzeneteket vagy értesítéseket."</string> - <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"A munkahelyi alkalmazások jelvénnyel vannak megjelölve, és ezeket láthatja a rendszergazda"</string> + <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"A munkahelyi alkalmazások jelvénnyel vannak megjelölve, és láthatók a rendszergazda számára"</string> <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Értem"</string> <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Munkahelyi alkalmazások szüneteltetése"</string> <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Folytatás"</string> diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index 442f4f62f4..5fee7a8fec 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Անձնական"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Աշխատանքային"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Զրույցներ"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Նշումների ստեղծում"</string> <string name="widget_education_header" msgid="4874760613775913787">"Բոլոր կարևոր տեղեկությունները՝ ձեռքի տակ"</string> <string name="widget_education_content" msgid="1731667670753497052">"Ավելացրեք վիջեթներ ձեր հիմնական էկրանին, որպեսզի տեղեկություններ ստանաք՝ առանց հավելվածները բացելու։"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Հպեք՝ վիջեթի կարգավորումները փոփոխելու համար"</string> diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index f16f69f7e1..7b33e9d042 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -35,7 +35,7 @@ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string> <string name="widget_accessible_dims_format" msgid="3640149169885301790">"lebar %1$d x tinggi %2$d"</string> <string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string> - <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Sentuh lama widget untuk memindah-mindahkannya di layar utama"</string> + <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Sentuh lama widget untuk memindahkannya di sekitar layar utama"</string> <string name="add_to_home_screen" msgid="9168649446635919791">"Tambahkan ke layar utama"</string> <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ditambahkan ke layar utama"</string> <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Saran"</string> @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Pribadi"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Kerja"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Percakapan"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Pembuatan catatan"</string> <string name="widget_education_header" msgid="4874760613775913787">"Info bermanfaat mudah dilihat"</string> <string name="widget_education_content" msgid="1731667670753497052">"Untuk mendapatkan info tanpa membuka aplikasi, Anda dapat menambahkan widget ke layar utama"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Ketuk untuk mengubah setelan widget"</string> @@ -85,7 +84,7 @@ <string name="gadget_error_text" msgid="740356548025791839">"Tidak dapat memuat widget"</string> <string name="gadget_setup_text" msgid="8348374825537681407">"Setelan widget"</string> <string name="gadget_complete_setup_text" msgid="309040266978007925">"Ketuk untuk menyelesaikan penyiapan"</string> - <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini adalah aplikasi sistem dan tidak dapat di-uninstal."</string> + <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini adalah aplikasi sistem dan tidak dapat dicopot pemasangannya."</string> <string name="folder_hint_text" msgid="5174843001373488816">"Sunting Nama"</string> <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> dinonaktifkan"</string> <string name="dotted_app_label" msgid="1865617679843363410">"{count,plural, =1{{app_name} memiliki # notifikasi}other{{app_name} memiliki # notifikasi}}"</string> diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index fc574cbf02..5e7af5f988 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Persónulegt"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Vinna"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Samtöl"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Glósugerð"</string> <string name="widget_education_header" msgid="4874760613775913787">"Gagnlegar upplýsingar innan seilingar"</string> <string name="widget_education_content" msgid="1731667670753497052">"Þú getur bætt við græjum á heimaskjáinn til að fá upplýsingar án þess að opna forrit"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Ýttu til að breyta græjustillingum"</string> diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 59c0cb771a..3251006a6a 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personali"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Lavoro"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversazioni"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Aggiunta di note"</string> <string name="widget_education_header" msgid="4874760613775913787">"Informazioni utili a portata di mano"</string> <string name="widget_education_content" msgid="1731667670753497052">"Per ricevere informazioni senza aprire le app, puoi aggiungere dei widget alla schermata Home"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tocca per modificare le impostazioni del widget"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Cartella: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o più elementi"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Sfondi"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Sfondo e stile"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Modifica la schermata Home"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Impostazioni schermata Home"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disattivata dall\'amministratore"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Consenti rotazione della schermata Home"</string> diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 92f5d1d59b..103e4e067a 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ווידג\'טים אישיים"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"עבודה"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"שיחות"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"כתיבת הערות"</string> <string name="widget_education_header" msgid="4874760613775913787">"קבלת מידע שימושי בהקשה"</string> <string name="widget_education_content" msgid="1731667670753497052">"רוצה לקבל מידע בלי לפתוח אפליקציות? אפשר להוסיף ווידג\'טים למסך הבית"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"אפשר לשנות את הגדרות הווידג\'ט בהקשה"</string> @@ -101,11 +100,12 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"תיקייה: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> פריטים או יותר"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"טפטים"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"טפט וסגנון"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"עריכה של מסך הבית"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"הגדרות של מסך הבית"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"הושבת על ידי מנהל המערכת שלך"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"אישור לסיבוב מסך הבית"</string> - <string name="allow_rotation_desc" msgid="8662546029078692509">"כאשר מסובבים את הטלפון"</string> + <string name="allow_rotation_desc" msgid="8662546029078692509">"כאשר הטלפון מסובב"</string> <string name="notification_dots_title" msgid="9062440428204120317">"סימני ההתראות"</string> <string name="notification_dots_desc_on" msgid="1679848116452218908">"מופעל"</string> <string name="notification_dots_desc_off" msgid="1760796511504341095">"כבוי"</string> diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 43cc1e3bf0..7552eaf01d 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"個人用"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"仕事用"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"会話"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"メモ"</string> <string name="widget_education_header" msgid="4874760613775913787">"ウィジェットで情報を得る"</string> <string name="widget_education_content" msgid="1731667670753497052">"ホーム画面にウィジェットを追加すると、アプリを開かずに情報を入手できます"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"タップしてウィジェットの設定を変更する"</string> diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index 7553f6a7b4..e56b58012a 100644 --- a/res/values-ka/strings.xml +++ b/res/values-ka/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"პირადი"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"სამსახური"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"მიმოწერები"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"ჩანიშვნა"</string> <string name="widget_education_header" msgid="4874760613775913787">"ადვილად მისაწვდომი სასარგებლო ინფორმაცია"</string> <string name="widget_education_content" msgid="1731667670753497052">"იმისთვის, რომ ინფორმაცია აპების გაუხსნელად მიიღოთ, შეგიძლიათ, მთავარ ეკრანზე ვიჯეტები დაამატოთ"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"შეეხეთ ვიჯეტის პარამეტრების შესაცვლელად"</string> @@ -160,7 +159,7 @@ <string name="all_apps_personal_tab" msgid="4190252696685155002">"პირადი"</string> <string name="all_apps_work_tab" msgid="4884822796154055118">"სამსახური"</string> <string name="work_profile_toggle_label" msgid="3081029915775481146">"სამსახურის პროფილი"</string> - <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"სამსახურის აპები ბეჯით არის მონიშნული და ხილულია თქვენი IT ადმინისტრატორისთვის"</string> + <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"სამსახურის აპები ბეჯით არის მონიშნული და ხილულია IT ადმინისტრატორისთვის"</string> <string name="work_profile_edu_accept" msgid="6069788082535149071">"გასაგებია"</string> <string name="work_apps_paused_title" msgid="3040901117349444598">"სამსახურის აპები დაპაუზებულია"</string> <string name="work_apps_paused_info_body" msgid="1687828929959237477">"თქვენ არ მიიღებთ შეტყობინებებს თქვენი სამსახურის აპებიდან"</string> diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index 00babfd2d3..4da44a8ea3 100644 --- a/res/values-kk/strings.xml +++ b/res/values-kk/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Жеке виджеттер"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Жұмыс виджеттері"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Әңгімелер"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Ескертпе жазу"</string> <string name="widget_education_header" msgid="4874760613775913787">"Саусақпен түртсеңіз болғаны – пайдалы ақпарат көз алдыңызда"</string> <string name="widget_education_content" msgid="1731667670753497052">"Қолданбаларды ашпай-ақ ақпарат алу үшін негізгі экранға тиісті виджеттерді қосыңыз."</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Виджет параметрлерін өзгерту үшін түртіңіз."</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> не одан көп элемент бар"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Тұсқағаздар"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Тұсқағаз және стиль"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Негізгі экранды өзгерту"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Негізгі экран параметрлері"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Әкімші өшірді"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Негізгі экранды бұруға рұқсат ету"</string> @@ -152,7 +152,7 @@ <string name="action_decrease_width" msgid="1374549771083094654">"Енін азайту"</string> <string name="action_decrease_height" msgid="282377193880900022">"Биіктігін азайту"</string> <string name="widget_resized" msgid="9130327887929620">"Виджет өлшемінің ені <xliff:g id="NUMBER_0">%1$s</xliff:g>, биіктігі <xliff:g id="NUMBER_1">%2$s</xliff:g> болып өзгертілді"</string> - <string name="action_deep_shortcut" msgid="2864038805849372848">"Жылдам пәрмендер"</string> + <string name="action_deep_shortcut" msgid="2864038805849372848">"Таңбашалар"</string> <string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Таңбашалар мен хабарландырулар"</string> <string name="action_dismiss_notification" msgid="5909461085055959187">"Бас тарту"</string> <string name="accessibility_close" msgid="2277148124685870734">"Жабу"</string> @@ -168,7 +168,7 @@ <string name="work_apps_paused_telephony_unavailable_body" msgid="8358872357502756790">"Жұмыс қолданбаларынан телефон қоңырауларын, мәтіндік хабарлар немесе хабарландырулар алмайсыз."</string> <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Жұмыс қолданбаларының танымбелгілері бар және олар әкімшіңізге көрінеді."</string> <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Түсінікті"</string> - <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Жұмыс қолданбаларын кідірту"</string> + <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Жұмыс қолданбаларын тоқтата тұру"</string> <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Қайта қосу"</string> <string name="developer_options_filter_hint" msgid="5896817443635989056">"Сүзгі"</string> <string name="search_pref_screen_title" msgid="3258959643336315962">"Телефоннан іздеу"</string> diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index 42e0d4bb1f..6aa399b95c 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ផ្ទាល់ខ្លួន"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ការងារ"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"ការសន្ទនា"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"ការកត់ត្រា"</string> <string name="widget_education_header" msgid="4874760613775913787">"ទទួលបានព័ត៌មានដែលមានប្រយោជន៍យ៉ាងងាយស្រួល"</string> <string name="widget_education_content" msgid="1731667670753497052">"ដើម្បីទទួលបានព័ត៌មានដោយមិនចាំបាច់បើកកម្មវិធី អ្នកអាចបញ្ចូលធាតុក្រាហ្វិកទៅក្នុងអេក្រង់ដើមរបស់អ្នក"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ចុចដើម្បីប្ដូរការកំណត់ធាតុក្រាហ្វិក"</string> diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index 481c42698b..e27483e503 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -50,18 +50,17 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ವೈಯಕ್ತಿಕ"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ಕೆಲಸ"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"ಸಂಭಾಷಣೆಗಳು"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"ಟಿಪ್ಪಣಿ ತೆಗೆದುಕೊಳ್ಳುವುದು"</string> <string name="widget_education_header" msgid="4874760613775913787">"ನಿಮ್ಮ ಬೆರಳ ತುದಿಯಲ್ಲಿ ಉಪಯುಕ್ತ ಮಾಹಿತಿ"</string> <string name="widget_education_content" msgid="1731667670753497052">"ಆ್ಯಪ್ಗಳನ್ನು ತೆರೆಯದೆಯೇ ಮಾಹಿತಿಯನ್ನು ಪಡೆಯಲು, ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್ನಲ್ಲಿ ನೀವು ವಿಜೆಟ್ಗಳನ್ನು ಸೇರಿಸಬಹುದು"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ವಿಜೆಟ್ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="widget_education_close_button" msgid="8676165703104836580">"ಅರ್ಥವಾಯಿತು"</string> <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ವಿಜೆಟ್ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಬದಲಾಯಿಸಿ"</string> - <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ಆ್ಯಪ್ಗಳನ್ನು ಹುಡುಕಿ"</string> + <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಹುಡುಕಿ"</string> <string name="all_apps_loading_message" msgid="5813968043155271636">"ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string> <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಕಂಡುಬಂದಿಲ್ಲ"</string> <string name="label_application" msgid="8531721983832654978">"ಆ್ಯಪ್"</string> <string name="all_apps_label" msgid="5015784846527570951">"ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು"</string> - <string name="notifications_header" msgid="1404149926117359025">"ನೋಟಿಫಿಕೇಶನ್ಗಳು"</string> + <string name="notifications_header" msgid="1404149926117359025">"ಅಧಿಸೂಚನೆಗಳು"</string> <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ಶಾರ್ಟ್ಕಟ್ ಸರಿಸಲು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string> <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ಶಾರ್ಟ್ಕಟ್ ಸರಿಸಲು ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಲು ಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string> <string name="out_of_space" msgid="6455557115204099579">"ಈ ಹೋಮ್ ಸ್ಕ್ರೀನ್ನಲ್ಲಿ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ"</string> @@ -106,13 +105,13 @@ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸಿ"</string> <string name="allow_rotation_desc" msgid="8662546029078692509">"ಫೋನ್ ತಿರುಗಿಸಿದಾಗ"</string> - <string name="notification_dots_title" msgid="9062440428204120317">"ನೋಟಿಫಿಕೇಶನ್ ಡಾಟ್ಗಳು"</string> + <string name="notification_dots_title" msgid="9062440428204120317">"ಅಧಿಸೂಚನೆ ಡಾಟ್ಗಳು"</string> <string name="notification_dots_desc_on" msgid="1679848116452218908">"ಆನ್ ಆಗಿದೆ"</string> <string name="notification_dots_desc_off" msgid="1760796511504341095">"ಆಫ್ ಆಗಿದೆ"</string> - <string name="title_missing_notification_access" msgid="7503287056163941064">"ನೋಟಿಫಿಕೇಶನ್ ಆ್ಯಕ್ಸೆಸ್ ಅಗತ್ಯವಿದೆ"</string> - <string name="msg_missing_notification_access" msgid="281113995110910548">"ನೋಟಿಫಿಕೇಶನ್ ಚುಕ್ಕೆಗಳನ್ನು ತೋರಿಸಲು, <xliff:g id="NAME">%1$s</xliff:g> ಗೆ ಆ್ಯಪ್ ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ಆನ್ ಮಾಡಿ"</string> + <string name="title_missing_notification_access" msgid="7503287056163941064">"ಅಧಿಸೂಚನೆ ಪ್ರವೇಶ ಅಗತ್ಯವಿದೆ"</string> + <string name="msg_missing_notification_access" msgid="281113995110910548">"ಅಧಿಸೂಚನೆ ಚುಕ್ಕೆಗಳನ್ನು ತೋರಿಸಲು, <xliff:g id="NAME">%1$s</xliff:g> ಗೆ ಅಪ್ಲಿಕೇಶನ್ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆನ್ ಮಾಡಿ"</string> <string name="title_change_settings" msgid="1376365968844349552">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಬದಲಾಯಿಸಿ"</string> - <string name="notification_dots_service_title" msgid="4284221181793592871">"ನೋಟಿಫಿಕೇಶನ್ ಡಾಟ್ಗಳನ್ನು ತೋರಿಸಿ"</string> + <string name="notification_dots_service_title" msgid="4284221181793592871">"ಅಧಿಸೂಚನೆ ಡಾಟ್ಗಳನ್ನು ತೋರಿಸಿ"</string> <string name="developer_options_title" msgid="700788437593726194">"ಡೆವಲಪರ್ ಆಯ್ಕೆಗಳು"</string> <string name="auto_add_shortcuts_label" msgid="4926805029653694105">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಆ್ಯಪ್ ಐಕಾನ್ಗಳನ್ನು ಸೇರಿಸಿ"</string> <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ಹೊಸ ಅಪ್ಲಿಕೇಶನ್ಗಳಿಗೆ"</string> diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index ab682a4862..b0bcff7cfd 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"개인 위젯"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"직장 위젯"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"대화"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"메모"</string> <string name="widget_education_header" msgid="4874760613775913787">"빠르게 유용한 정보 확인"</string> <string name="widget_education_content" msgid="1731667670753497052">"앱을 열지 않고 정보를 확인하려면 홈 화면에 위젯을 추가하세요."</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"탭하여 위젯 설정 변경"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"폴더: <xliff:g id="NAME">%1$s</xliff:g>, 항목 <xliff:g id="SIZE">%2$d</xliff:g>개 이상"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"배경화면"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"배경화면 및 스타일"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"홈 화면 수정"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"홈 설정"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"관리자가 사용 중지함"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"홈 화면 회전 허용"</string> diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index 20476912a1..24fca36e2c 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Жеке виджеттер"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Жумуш"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Сүйлөшүүлөр"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Эскертме жазуу"</string> <string name="widget_education_header" msgid="4874760613775913787">"Керектүү маалымат манжаңыздын учунда"</string> <string name="widget_education_content" msgid="1731667670753497052">"Бир нерсе билүү үчүн колдонмолорду улам ачып убара болбостон, башкы экранга виджеттерди кошуп коюңуз."</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Виджеттин параметрлерин өзгөртүү үчүн таптап коюңуз"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"<xliff:g id="NAME">%1$s</xliff:g> папкасындагы объекттер: <xliff:g id="SIZE">%2$d</xliff:g> же андан көбүрөөк"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Тушкагаз жана стиль"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Башкы экранды түзөтүү"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Башкы бет параметрлери"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администраторуңуз өчүрүп койгон"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Башкы экранды бурууга уруксат берүү"</string> diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index 166c37005b..21481787a5 100644 --- a/res/values-lo/strings.xml +++ b/res/values-lo/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ສ່ວນຕົວ"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ວຽກ"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"ການສົນທະນາ"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"ການຈົດບັນທຶກ"</string> <string name="widget_education_header" msgid="4874760613775913787">"ຂໍ້ມູນທີ່ເປັນປະໂຫຍດຢູ່ປາຍນິ້ວຂອງທ່ານ"</string> <string name="widget_education_content" msgid="1731667670753497052">"ເພື່ອຮັບຂໍ້ມູນໂດຍບໍ່ຕ້ອງເປີດແອັບ, ທ່ານສາມາດເພີ່ມວິດເຈັດໃສ່ໂຮມສະກຣີນຂອງທ່ານໄດ້"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ແຕະເພື່ອປ່ຽນການຕັ້ງຄ່າວິດເຈັດ"</string> diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 4c674981eb..2ea7174933 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Asmeniniai"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Darbas"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Pokalbiai"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Užrašų kūrimas"</string> <string name="widget_education_header" msgid="4874760613775913787">"Lengvai pasiekiama naudinga informacija"</string> <string name="widget_education_content" msgid="1731667670753497052">"Jei norite gauti informacijos neatidarę programų, galite pridėti valdiklių pagrindiniame ekrane"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Palieskite, kad pakeistumėte valdiklio nustatymus"</string> @@ -70,7 +69,7 @@ <string name="all_apps_search_results" msgid="5889367432531296759">"Paieškos rezultatai"</string> <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Asmeninių programų sąrašas"</string> <string name="all_apps_button_work_label" msgid="7270707118948892488">"Darbo programų sąrašas"</string> - <string name="remove_drop_target_label" msgid="7812859488053230776">"Pašalinti"</string> + <string name="remove_drop_target_label" msgid="7812859488053230776">"Ištrinti"</string> <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Pašalinti"</string> <string name="app_info_drop_target_label" msgid="692894985365717661">"Programos inform."</string> <string name="install_drop_target_label" msgid="2539096853673231757">"Įdiegti"</string> diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index ad7af542f2..3a848dd229 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personīgs"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Darba"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Sarunas"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Piezīmju pierakstīšana"</string> <string name="widget_education_header" msgid="4874760613775913787">"Ērta piekļuve noderīgai informācijai"</string> <string name="widget_education_content" msgid="1731667670753497052">"Lai iegūtu informāciju, neatverot lietotnes, varat pievienot sākuma ekrānam logrīkus"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Pieskarieties, lai mainītu logrīka iestatījumus."</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mape <xliff:g id="NAME">%1$s</xliff:g>, vienumu skaits mapē: vismaz <xliff:g id="SIZE">%2$d</xliff:g>"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Fona tapetes"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fona tapete un stils"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Rediģēt sākuma ekrānu"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Sākumlapas iestatījumi"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Atspējojis administrators"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Atļaut sākuma ekrāna pagriešanu"</string> diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index c2ee822bfd..6de6141875 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Лични"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Работни"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Разговори"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Фаќање белешки"</string> <string name="widget_education_header" msgid="4874760613775913787">"Корисни информации на дофат на прстите"</string> <string name="widget_education_content" msgid="1731667670753497052">"За да добивате информации без да ги отворате апликациите, може да додадете виџети на почетниот екран"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Допрете за да ги промените поставките за виџетот"</string> @@ -83,7 +82,7 @@ <string name="permlab_write_settings" msgid="4820028712156303762">"да пишува поставки и кратенки на почетна страница"</string> <string name="permdesc_write_settings" msgid="726859348127868466">"Овозможува апликацијата да ги менува поставките и кратенките на почетната страница."</string> <string name="gadget_error_text" msgid="740356548025791839">"Не може да се вчита виџетот"</string> - <string name="gadget_setup_text" msgid="8348374825537681407">"Поставки за виџетот"</string> + <string name="gadget_setup_text" msgid="8348374825537681407">"Поставки за виџет"</string> <string name="gadget_complete_setup_text" msgid="309040266978007925">"Допрете за да го завршите поставувањето"</string> <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ова е системска апликација и не може да се деинсталира."</string> <string name="folder_hint_text" msgid="5174843001373488816">"Изменете го името"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> или повеќе ставки"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Тапет и стил"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Изменете го почетниот екран"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Поставки за почетен екран"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Оневозможено од администраторот"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Дозволи ротирање на почетниот екран"</string> @@ -171,7 +171,7 @@ <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Паузирај ги работните апликации"</string> <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Прекини ја паузата"</string> <string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтер"</string> - <string name="search_pref_screen_title" msgid="3258959643336315962">"Пребарување низ телефонот"</string> - <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Пребарување низ таблетот"</string> + <string name="search_pref_screen_title" msgid="3258959643336315962">"Пребарувајте на телефонот"</string> + <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Пребарувајте на таблетот"</string> <string name="remote_action_failed" msgid="1383965239183576790">"Не успеа: <xliff:g id="WHAT">%1$s</xliff:g>"</string> </resources> diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index 3600eddf53..59827f28eb 100644 --- a/res/values-ml/strings.xml +++ b/res/values-ml/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"വ്യക്തിപരം"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ജോലി"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"സംഭാഷണങ്ങൾ"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"കുറിപ്പ് രേഖപ്പെടുത്തൽ"</string> <string name="widget_education_header" msgid="4874760613775913787">"ഉപകാരപ്രദമായ വിവരങ്ങൾ നിങ്ങളുടെ വിരൽത്തുമ്പിൽ"</string> <string name="widget_education_content" msgid="1731667670753497052">"ആപ്പുകൾ തുറക്കാതെ വിവരങ്ങൾ ലഭിക്കാൻ, നിങ്ങൾക്ക് ഹോം സ്ക്രീനിലേക്ക് വിജറ്റുകൾ ചേർക്കാം"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"വിജറ്റ് ക്രമീകരണം മാറ്റാൻ ടാപ്പ് ചെയ്യുക"</string> @@ -120,7 +119,7 @@ <string name="abandoned_clean_this" msgid="7610119707847920412">"നീക്കംചെയ്യുക"</string> <string name="abandoned_search" msgid="891119232568284442">"തിരയുക"</string> <string name="abandoned_promises_title" msgid="7096178467971716750">"ഈ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്തിട്ടില്ല"</string> - <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ഈ ഐക്കണുവേണ്ടി അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്തിട്ടില്ല. നിങ്ങൾക്കത് നീക്കംചെയ്യാനാകും അല്ലെങ്കിൽ അപ്ലിക്കേഷനുവേണ്ടി തിരഞ്ഞുകൊണ്ട് അത് സ്വയമേവ ഇൻസ്റ്റാളുചെയ്യുക."</string> + <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ഈ ഐക്കണുവേണ്ടി അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്തിട്ടില്ല. നിങ്ങൾക്കത് നീക്കംചെയ്യാനാകും അല്ലെങ്കിൽ അപ്ലിക്കേഷനുവേണ്ടി തിരഞ്ഞുകൊണ്ട് അത് സ്വമേധയാ ഇൻസ്റ്റാളുചെയ്യുക."</string> <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ഇൻസ്റ്റാൾ ചെയ്യുന്നു, <xliff:g id="PROGRESS">%2$s</xliff:g> പൂർത്തിയായി"</string> <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ഡൗൺലോഡ് ചെയ്യുന്നു, <xliff:g id="PROGRESS">%2$s</xliff:g> പൂർത്തിയായി"</string> <string name="app_waiting_download_title" msgid="7053938513995617849">"ഇൻസ്റ്റാൾ ചെയ്യാൻ <xliff:g id="NAME">%1$s</xliff:g> കാക്കുന്നു"</string> diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index 430e80d669..eea9195d18 100644 --- a/res/values-mn/strings.xml +++ b/res/values-mn/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Хувийн виджетүүд"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Ажил"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Харилцан яриа"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Тэмдэглэл хөтлөх"</string> <string name="widget_education_header" msgid="4874760613775913787">"Хэрэгтэй мэдээллээ хурууныхаа үзүүрээр аваарай"</string> <string name="widget_education_content" msgid="1731667670753497052">"Аппуудыг нээлгүйгээр мэдээлэл авахын тулд та үндсэн нүүрэндээ виджетүүд нэмэх боломжтой"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Жижиг хэрэгслийн тохиргоог өөрчлөхийн тулд товшино уу"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> эсвэл үүнээс олон зүйл"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Дэлгэцийн зураг"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Дэлгэцийн зураг, загвар"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Үндсэн нүүрийг засах"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Нүүр хуудасны тохиргоо"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Таны админ идэвхгүй болгосон"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Үндсэн нүүрийг эргүүлэхийг зөвшөөрөх"</string> diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index b4515a09b8..b74a8f50ed 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"वैयक्तिक"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ऑफिस"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"संभाषणे"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"टिपा घेणे"</string> <string name="widget_education_header" msgid="4874760613775913787">"तुमच्यासाठी सहज उपलब्ध असलेली माहिती"</string> <string name="widget_education_content" msgid="1731667670753497052">"ॲप्स न उघडता माहिती मिळवण्यासाठी, तुम्ही तुमच्या होम स्क्रीनवर विजेट जोडू शकता"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"विजेट सेटिंग्ज बदलण्यासाठी टॅप करा"</string> @@ -89,9 +88,9 @@ <string name="folder_hint_text" msgid="5174843001373488816">"नाव संपादित करा"</string> <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> अक्षम केला आहे"</string> <string name="dotted_app_label" msgid="1865617679843363410">"{count,plural, =1{{app_name} संबंधित # सूचना आहे}other{{app_name} संबंधित # सूचना आहेत}}"</string> - <string name="default_scroll_format" msgid="7475544710230993317">"%2$d पैकी %1$d पेज"</string> + <string name="default_scroll_format" msgid="7475544710230993317">"%2$d पैकी %1$d पृष्ठ"</string> <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d पैकी %1$d मुख्य स्क्रीन"</string> - <string name="workspace_new_page" msgid="257366611030256142">"नवीन होम स्क्रीन पेज"</string> + <string name="workspace_new_page" msgid="257366611030256142">"नवीन मुख्य स्क्रीन पृष्ठ"</string> <string name="folder_opened" msgid="94695026776264709">"फोल्डर उघडले, <xliff:g id="WIDTH">%1$d</xliff:g> बाय <xliff:g id="HEIGHT">%2$d</xliff:g>"</string> <string name="folder_tap_to_close" msgid="4625795376335528256">"फोल्डर बंद करण्यासाठी टॅप करा"</string> <string name="folder_tap_to_rename" msgid="4017685068016979677">"पुनर्नामित करणे सेव्ह करण्यासाठी टॅप करा"</string> diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index 5dcc913a7a..7d1a4d172a 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Peribadi"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Tempat kerja"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Perbualan"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Pengambilan nota"</string> <string name="widget_education_header" msgid="4874760613775913787">"Maklumat berguna di hujung jari anda"</string> <string name="widget_education_content" msgid="1731667670753497052">"Untuk mendapatkan maklumat tanpa membuka apl, anda boleh menambahkan widget pada skrin utama anda"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Ketik untuk menukar tetapan widget"</string> @@ -100,7 +99,7 @@ <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item"</string> <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> atau lebih banyak item"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Hiasan latar"</string> - <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Hiasan latar & gaya"</string> + <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Kertas dinding & gaya"</string> <string name="edit_home_screen" msgid="8947858375782098427">"Edit Skrin Utama"</string> <string name="settings_button_text" msgid="8873672322605444408">"Tetapan skrin utama"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dilumpuhkan oleh pentadbir anda"</string> @@ -171,7 +170,7 @@ <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Jeda apl kerja"</string> <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Nyahjeda"</string> <string name="developer_options_filter_hint" msgid="5896817443635989056">"Tapis"</string> - <string name="search_pref_screen_title" msgid="3258959643336315962">"Cari pada telefon"</string> - <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Cari pada tablet anda"</string> + <string name="search_pref_screen_title" msgid="3258959643336315962">"Cari telefon anda"</string> + <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Cari tablet anda"</string> <string name="remote_action_failed" msgid="1383965239183576790">"Gagal: <xliff:g id="WHAT">%1$s</xliff:g>"</string> </resources> diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index e0e98d54d3..1a6b0de1e6 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ကိုယ်ပိုင်"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"အလုပ်"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"စကားဝိုင်းများ"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"မှတ်စုလိုက်ခြင်း"</string> <string name="widget_education_header" msgid="4874760613775913787">"အသုံးဝင်သော အချက်အလက်များကို အလွယ်တကူ ရယူလိုက်ပါ"</string> <string name="widget_education_content" msgid="1731667670753497052">"အက်ပ်မဖွင့်ဘဲ အချက်အလက်များရယူရန် ပင်မစာမျက်နှာတွင် ဝိဂျက်များ ထည့်နိုင်သည်"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ဝိဂျက် ဆက်တင်များကို ပြောင်းရန် တို့ပါ"</string> diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 485f8458e5..41fb8c084d 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personlige"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Jobb"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Samtaler"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Notatskriving"</string> <string name="widget_education_header" msgid="4874760613775913787">"Lett tilgjengelig nyttig informasjon"</string> <string name="widget_education_content" msgid="1731667670753497052">"For å se informasjon uten å åpne apper kan du legge til moduler på startskjermen"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Trykk for å endre modulinnstillinger"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller flere elementer"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunner"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Bakgrunn og stil"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Endre startsiden"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Startsideinnstillinger"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratoren har slått av funksjonen"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Tillat at startskjermen roterer"</string> diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index 4219dcecc8..8ce5603451 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -50,13 +50,12 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"व्यक्तिगत"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"कामसम्बन्धी"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"वार्तालापहरू"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"नोट लेख्ने कार्य"</string> <string name="widget_education_header" msgid="4874760613775913787">"उपयोगी जानकारी सजिलै प्राप्त गर्नुहोस्"</string> <string name="widget_education_content" msgid="1731667670753497052">"एपहरू नखोलिकनै जानकारी प्राप्त गर्न तपाईं आफ्नो होम स्क्रिनमा विजेटहरू हाल्न सक्नुहुन्छ"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"विजेटका सेटिङ बदल्न ट्याप गर्नुहोस्"</string> <string name="widget_education_close_button" msgid="8676165703104836580">"बुझेँ"</string> <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"विजेटका सेटिङ बदल्नुहोस्"</string> - <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"एपहरू खोज्नुहोस्"</string> + <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"खोजसम्बन्धी एपहरू"</string> <string name="all_apps_loading_message" msgid="5813968043155271636">"एपहरू लोड गर्दै…"</string> <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" सँग मिल्दो कुनै एप भेटिएन"</string> <string name="label_application" msgid="8531721983832654978">"एप"</string> @@ -105,7 +104,7 @@ <string name="settings_button_text" msgid="8873672322605444408">"होम पेजका सेटिङहरू"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"तपाईँको प्रशासकद्वारा असक्षम गरिएको"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"होम स्क्रिन रोटेट हुन दिइयोस्"</string> - <string name="allow_rotation_desc" msgid="8662546029078692509">"फोन घुमाउँदा"</string> + <string name="allow_rotation_desc" msgid="8662546029078692509">"फोनलाई घुमाइँदा"</string> <string name="notification_dots_title" msgid="9062440428204120317">"सूचनाको प्रतीक जनाउने थोप्लाहरू"</string> <string name="notification_dots_desc_on" msgid="1679848116452218908">"सक्रिय"</string> <string name="notification_dots_desc_off" msgid="1760796511504341095">"निष्क्रिय"</string> @@ -172,6 +171,6 @@ <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"सुचारु गर्नुहोस्"</string> <string name="developer_options_filter_hint" msgid="5896817443635989056">"फिल्टर"</string> <string name="search_pref_screen_title" msgid="3258959643336315962">"आफ्नो फोन खोज्नुहोस्"</string> - <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"आफ्नो ट्याबलेटमा खोज्नुहोस्"</string> + <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"आफ्नो ट्याब्लेट खोज्नुहोस्"</string> <string name="remote_action_failed" msgid="1383965239183576790">"कार्य पूरा गर्न सकिएन: <xliff:g id="WHAT">%1$s</xliff:g>"</string> </resources> diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 416d82d1be..2241a58b87 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Persoonlijk"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Werk"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Gesprekken"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Aantekeningen maken"</string> <string name="widget_education_header" msgid="4874760613775913787">"Nuttige informatie binnen handbereik"</string> <string name="widget_education_content" msgid="1731667670753497052">"Als je informatie wilt krijgen zonder apps te openen, kun je widgets toevoegen aan je startscherm"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tik om de widgetinstellingen te wijzigen"</string> diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml index 8bfe424501..5a93d91a83 100644 --- a/res/values-or/strings.xml +++ b/res/values-or/strings.xml @@ -27,7 +27,7 @@ <string name="safemode_widget_error" msgid="4863470563535682004">"ନିରାପଦ ମୋଡରେ ୱିଜେଟ୍ ଅକ୍ଷମ କରାଗଲା"</string> <string name="shortcut_not_available" msgid="2536503539825726397">"ଶର୍ଟକଟ୍ ଉପଲବ୍ଧ ନାହିଁ"</string> <string name="home_screen" msgid="5629429142036709174">"ହୋମ"</string> - <string name="recent_task_option_split_screen" msgid="6690461455618725183">"ସ୍କ୍ରିନକୁ ସ୍ପ୍ଲିଟ କରନ୍ତୁ"</string> + <string name="recent_task_option_split_screen" msgid="6690461455618725183">"ସ୍କ୍ରିନକୁ ସ୍ପ୍ଲିଟ୍ କରନ୍ତୁ"</string> <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ପାଇଁ ଆପ ସୂଚନା"</string> <string name="save_app_pair" msgid="5647523853662686243">"ଆପ ପେୟାର ସେଭ କରନ୍ତୁ"</string> <string name="long_press_widget_to_add" msgid="3587712543577675817">"ଏକ ୱିଜେଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ।"</string> @@ -39,7 +39,7 @@ <string name="add_to_home_screen" msgid="9168649446635919791">"ହୋମ ସ୍କ୍ରିନରେ ଯୋଗ କରନ୍ତୁ"</string> <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>ର ୱିଜେଟ ହୋମ ସ୍କ୍ରିନରେ ଯୋଡ଼ାଗଲା"</string> <string name="suggested_widgets_header_title" msgid="1844314680798145222">"ପରାମର୍ଶଗୁଡ଼ିକ"</string> - <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ୱିଜେଟ}other{# ୱିଜେଟ}}"</string> + <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{#ଟି ୱିଜେଟ୍}other{#ଟି ୱିଜେଟ୍}}"</string> <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{#ଟି ସର୍ଟକଟ୍}other{#ଟି ସର୍ଟକଟ୍}}"</string> <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string> <string name="widget_button_text" msgid="2880537293434387943">"ୱିଜେଟ୍"</string> @@ -50,13 +50,12 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ବ୍ୟକ୍ତିଗତ"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ୱାର୍କ"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"ନୋଟ-ଟେକିଂ"</string> <string name="widget_education_header" msgid="4874760613775913787">"ଉପଯୋଗୀ ସୂଚନା ଆପଣଙ୍କ ପାଖରେ ସହଜରେ ଉପଲବ୍ଧ"</string> <string name="widget_education_content" msgid="1731667670753497052">"ଆପ୍ସକୁ ନଖୋଲି ସୂଚନା ପାଇବା ପାଇଁ, ଆପଣ ଆପଣଙ୍କ ହୋମ ସ୍କ୍ରିନରେ ୱିଜେଟଗୁଡ଼ିକୁ ଯୋଗ କରିପାରିବେ"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ୱିଜେଟ ସେଟିଂସ ପରିବର୍ତ୍ତନ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string> <string name="widget_education_close_button" msgid="8676165703104836580">"ବୁଝିଗଲି"</string> <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ୱିଜେଟ ସେଟିଂସ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string> - <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ଆପ ସର୍ଚ୍ଚ କରନ୍ତୁ"</string> + <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ଆପ୍ ଖୋଜନ୍ତୁ"</string> <string name="all_apps_loading_message" msgid="5813968043155271636">"ଆପ୍ ଲୋଡ୍ ହେଉଛି..."</string> <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ସହିତ ମେଳ ହେଉଥିବା କୌଣସି ଆପ୍ ମିଳିଲା ନାହିଁ"</string> <string name="label_application" msgid="8531721983832654978">"ଆପ୍"</string> @@ -70,7 +69,7 @@ <string name="all_apps_search_results" msgid="5889367432531296759">"ସନ୍ଧାନ ଫଳାଫଳ"</string> <string name="all_apps_button_personal_label" msgid="1315764287305224468">"ବ୍ୟକ୍ତିଗତ ଆପ୍ ତାଲିକା"</string> <string name="all_apps_button_work_label" msgid="7270707118948892488">"କାର୍ଯ୍ୟକାରୀ ଆପ୍ ତାଲିକା"</string> - <string name="remove_drop_target_label" msgid="7812859488053230776">"କାଢ଼ି ଦିଅନ୍ତୁ"</string> + <string name="remove_drop_target_label" msgid="7812859488053230776">"ବାହାର କରନ୍ତୁ"</string> <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ଅନଇନଷ୍ଟଲ କରନ୍ତୁ"</string> <string name="app_info_drop_target_label" msgid="692894985365717661">"ଆପ୍ ସୂଚନା"</string> <string name="install_drop_target_label" msgid="2539096853673231757">"ଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"ଫୋଲ୍ଡର୍: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> କିମ୍ବା ଅଧିକ ଆଇଟମ୍"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"ୱାଲପେପର୍"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ୱାଲପେପର ଏବଂ ଷ୍ଟାଇଲ"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"ହୋମ ସ୍କ୍ରିନକୁ ଏଡିଟ କରନ୍ତୁ"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"ହୋମ ସେଟିଂସ"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ଆପଣଙ୍କ ଆଡମିନଙ୍କ ଦ୍ୱାରା ଅକ୍ଷମ କରାଯାଇଛି"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"ହୋମ ସ୍କ୍ରିନ ରୋଟେସନକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> @@ -160,7 +160,7 @@ <string name="all_apps_personal_tab" msgid="4190252696685155002">"ବ୍ୟକ୍ତିଗତ"</string> <string name="all_apps_work_tab" msgid="4884822796154055118">"ୱାର୍କ"</string> <string name="work_profile_toggle_label" msgid="3081029915775481146">"ୱର୍କ ପ୍ରୋଫାଇଲ୍"</string> - <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ୱାର୍କ ଆପ୍ସକୁ ବେଜ କରାଯାଇଛି ଏବଂ ସେଗୁଡ଼ିକ ଆପଣଙ୍କ IT ଆଡମିନଙ୍କୁ ଦେଖାଯାଉଛି"</string> + <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ୱାର୍କ ଆପ୍ସ ବ୍ୟାଜ କରାଯାଇଛି ଏବଂ ସେଗୁଡ଼ିକ ଆପଣଙ୍କ IT ଆଡମିନଙ୍କୁ ଦୃଶ୍ୟମାନ ହେଉଛି"</string> <string name="work_profile_edu_accept" msgid="6069788082535149071">"ବୁଝିଗଲି"</string> <string name="work_apps_paused_title" msgid="3040901117349444598">"ୱାର୍କ ଆପ୍ସ ବିରତ କରାଯାଇଛି"</string> <string name="work_apps_paused_info_body" msgid="1687828929959237477">"ଆପଣ ଆପଣଙ୍କ ୱାର୍କ ଆପ୍ସରୁ ବିଜ୍ଞପ୍ତି ପାଇବେ ନାହିଁ"</string> diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index f9a66dfdbf..ae10c8f068 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ਨਿੱਜੀ"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ਕਾਰਜ-ਸਥਾਨ"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"ਗੱਲਾਂਬਾਤਾਂ"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"ਨੋਟ ਬਣਾਉਣਾ"</string> <string name="widget_education_header" msgid="4874760613775913787">"ਮਹੱਤਵਪੂਰਨ ਜਾਣਕਾਰੀ ਤੁਰੰਤ ਪ੍ਰਾਪਤ ਕਰੋ"</string> <string name="widget_education_content" msgid="1731667670753497052">"ਐਪਾਂ ਨੂੰ ਖੋਲ੍ਹੇ ਬਿਨਾਂ ਜਾਣਕਾਰੀ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ, ਤੁਸੀਂ ਆਪਣੀ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਜੇਟ ਸ਼ਾਮਲ ਕਰ ਸਕਦੇ ਹੋ"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ਵਿਜੇਟ ਸੈਟਿੰਗਾਂ ਨੂੰ ਬਦਲਣ ਲਈ ਟੈਪ ਕਰੋ"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ਜਾਂ ਹੋਰ ਆਈਟਮਾਂ"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"ਵਾਲਪੇਪਰ"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ਵਾਲਪੇਪਰ ਅਤੇ ਸਟਾਈਲ"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"ਹੋਮ ਸਕ੍ਰੀਨ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"ਹੋਮ ਸੈਟਿੰਗਾਂ"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"ਹੋਮ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ"</string> diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 4f3abe3356..6f73d5fdfe 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -35,7 +35,7 @@ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string> <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Szerokość %1$d, wysokość %2$d"</string> <string name="widget_preview_context_description" msgid="9045841361655787574">"Widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string> - <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Aby poruszać widżetem po ekranie głównym, kliknij go i przytrzymaj"</string> + <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Kliknij i przytrzymaj widżet, aby poruszać nim po ekranie głównym"</string> <string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj do ekranu głównego"</string> <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> został dodany do ekranu głównego"</string> <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugestie"</string> @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Osobiste"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Służbowe"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Rozmowy"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Notatki"</string> <string name="widget_education_header" msgid="4874760613775913787">"Użyteczne informacje w zasięgu ręki"</string> <string name="widget_education_content" msgid="1731667670753497052">"Możesz dodać widżety do ekranu głównego, aby uzyskiwać informacje bez otwierania aplikacji"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Kliknij, aby zmienić ustawienia widżetu"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, liczba elementów: <xliff:g id="SIZE">%2$d</xliff:g> lub więcej"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Tapeta i styl"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Edytuj ekran główny"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Ustawienia ekranu głównego"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Funkcja wyłączona przez administratora"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Zezwalaj na obrót ekranu głównego"</string> diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 0acf63ecd9..dcbbe1dd3c 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Pessoais"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabalho"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversas"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Tomar notas"</string> <string name="widget_education_header" msgid="4874760613775913787">"Informações úteis à sua disposição"</string> <string name="widget_education_content" msgid="1731667670753497052">"Para obter informações sem abrir apps, pode adicionar widgets ao seu ecrã principal"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toque para alterar as definições do widget"</string> diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index e0c9b84a62..421f2dd24e 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Pessoais"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabalho"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversas"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Anotações"</string> <string name="widget_education_header" msgid="4874760613775913787">"Informações úteis ao seu alcance"</string> <string name="widget_education_content" msgid="1731667670753497052">"Para ver informações sem precisar abrir os apps, adicione widgets à sua tela inicial"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toque para mudar as configurações do widget"</string> @@ -107,7 +106,7 @@ <string name="allow_rotation_title" msgid="7222049633713050106">"Permitir a rotação da tela inicial"</string> <string name="allow_rotation_desc" msgid="8662546029078692509">"Quando o smartphone for girado"</string> <string name="notification_dots_title" msgid="9062440428204120317">"Pontos de notificação"</string> - <string name="notification_dots_desc_on" msgid="1679848116452218908">"Ativados"</string> + <string name="notification_dots_desc_on" msgid="1679848116452218908">"Ativado"</string> <string name="notification_dots_desc_off" msgid="1760796511504341095">"Desativado"</string> <string name="title_missing_notification_access" msgid="7503287056163941064">"Acesso a notificações necessário"</string> <string name="msg_missing_notification_access" msgid="281113995110910548">"Para mostrar pontos de notificação, ative as notificações de app para <xliff:g id="NAME">%1$s</xliff:g>"</string> diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index d6c5e5e5d7..0f2c079952 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personale"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Serviciu"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversații"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Luare de notițe"</string> <string name="widget_education_header" msgid="4874760613775913787">"Informații utile la îndemâna ta"</string> <string name="widget_education_content" msgid="1731667670753497052">"Pentru a primi informații fără să deschizi aplicațiile, poți adăuga widgeturi pe ecranul de pornire"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Atinge ca să schimbi setările pentru widgeturi"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Dosar: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> sau mai multe elemente"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Imagini de fundal"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Imagine de fundal și stil"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Editează ecranul de pornire"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Setări ecran de pornire"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dezactivată de administrator"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Permite rotirea ecranului de pornire"</string> diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 6315ee5713..5d67c6aeb1 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Личные виджеты"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Рабочие виджеты"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Разговоры"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Создание заметок"</string> <string name="widget_education_header" msgid="4874760613775913787">"Вся нужная информация перед глазами"</string> <string name="widget_education_content" msgid="1731667670753497052">"Чтобы не открывать приложения каждый раз, когда нужна информация, добавьте виджеты на главный экран."</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Нажмите, чтобы изменить настройки виджета"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\" (объектов: <xliff:g id="SIZE">%2$d</xliff:g> или больше)"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Обои"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Обои и стиль"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Изменить главный экран"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Главный экран"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Функция отключена администратором"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Разрешить поворачивать главный экран"</string> diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index 15bc4a7594..c11b8d4d93 100644 --- a/res/values-si/strings.xml +++ b/res/values-si/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"පුද්ගලික"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"කාර්යාලය"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"සංවාද"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"සටහන් කර ගැනීම"</string> <string name="widget_education_header" msgid="4874760613775913787">"ප්රයෝජනවත් තොරතුරු ඔබගේ ඇඟිලි තුඩු අග"</string> <string name="widget_education_content" msgid="1731667670753497052">"යෙදුම් විවෘත නොකර තොරතුරු ලබා ගැනීම සඳහා, ඔබට ඔබගේ මුල් තිරයට විජට් එක් කළ හැකිය"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"විජට් සැකසීම් වෙනස් කිරීමට තට්ටු කරන්න"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"ෆෝල්ඩර: <xliff:g id="NAME">%1$s</xliff:g>, අයිතම <xliff:g id="SIZE">%2$d</xliff:g>ක් හෝ වැඩි ගණනක්"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"වෝල්පේපර"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"වෝල්පේපරය සහ මෝස්තරය"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"මුල් තිරය සංස්කරණය කරන්න"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"නිවසේ සැකසීම්"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ඔබගේ පරිපාලක විසින් අබල කරන ලදී"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"මුල් තිරය කරකැවීමට ඉඩ දෙන්න"</string> diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 2028c35a99..434e416327 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Osobné"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Práca"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Konverzácie"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Zapisovanie poznámok"</string> <string name="widget_education_header" msgid="4874760613775913787">"Užitočné informácie poruke"</string> <string name="widget_education_content" msgid="1731667670753497052">"Ak chcete získavať informácie bez otvárania aplikácií, môžete si na plochu pridať miniaplikácie"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Klepnutím zmeňte nastavenia miniaplikácie"</string> diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index e6197c7aa3..cad7f40064 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Osebni"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Služba"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Pogovori"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Ustvarjanje zapiskov"</string> <string name="widget_education_header" msgid="4874760613775913787">"Koristne informacije na dosegu prstov"</string> <string name="widget_education_content" msgid="1731667670753497052">"Če si želite podatke ogledati brez odpiranja aplikacij, lahko na začetni zaslon dodate pripomočke."</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Dotaknite se, če želite spremeniti nastavitve pripomočka."</string> diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index c6b8e7ca9d..ff97f00143 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personale"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Puna"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Bisedat"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Mbajtja e shënimeve"</string> <string name="widget_education_header" msgid="4874760613775913787">"Informacione të dobishme në majë të gishtave të tu"</string> <string name="widget_education_content" msgid="1731667670753497052">"Për të marrë informacione pa i hapur aplikacionet, mund të shtosh miniaplikacione në ekranin bazë"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Trokit për të ndryshuar cilësimet e miniaplikacionit"</string> diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index a4d7795d27..1cfda6b051 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Лично"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Посао"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Конверзације"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Прављење бележака"</string> <string name="widget_education_header" msgid="4874760613775913787">"Корисне информације надохват руке"</string> <string name="widget_education_content" msgid="1731667670753497052">"Да бисте пронашли информације без отварања апликација, можете да додате виџете на почетни екран"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Додирните да бисте променили подешавања виџета"</string> @@ -157,8 +156,8 @@ <string name="action_dismiss_notification" msgid="5909461085055959187">"Одбаци"</string> <string name="accessibility_close" msgid="2277148124685870734">"Затвори"</string> <string name="notification_dismissed" msgid="6002233469409822874">"Обавештење је одбачено"</string> - <string name="all_apps_personal_tab" msgid="4190252696685155002">"Лично"</string> - <string name="all_apps_work_tab" msgid="4884822796154055118">"Посао"</string> + <string name="all_apps_personal_tab" msgid="4190252696685155002">"Личне"</string> + <string name="all_apps_work_tab" msgid="4884822796154055118">"Пословне"</string> <string name="work_profile_toggle_label" msgid="3081029915775481146">"Пословни профил"</string> <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Пословне апликације су означене значком и ИТ администратор може да их види"</string> <string name="work_profile_edu_accept" msgid="6069788082535149071">"Важи"</string> @@ -169,7 +168,7 @@ <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Пословне апликације су означене значком и ИТ администратор може да их види"</string> <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Важи"</string> <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Паузирај пословне апликације"</string> - <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Поново активирај"</string> + <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Опозови паузу"</string> <string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтер"</string> <string name="search_pref_screen_title" msgid="3258959643336315962">"Претражите телефон"</string> <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Претражите таблет"</string> diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index cd946e78d2..8c918a0a2c 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Privata"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Arbete"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Konversationer"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Anteckna"</string> <string name="widget_education_header" msgid="4874760613775913787">"Användbar information nära till hands"</string> <string name="widget_education_content" msgid="1731667670753497052">"Om du vill ha information utan att öppna appar kan du lägga till widgetar på startskärmen"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tryck för att ändra inställningarna för widgeten"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mapp: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller fler objekt"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunder"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Bakgrund och utseende"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Redigera startskärm"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Startinställningar"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inaktiverat av administratören"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Tillåt rotering av startskärmen"</string> diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 2686f8a8e3..b992037748 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Binafsi"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Kazini"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Mazungumzo"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Kuandika madokezo"</string> <string name="widget_education_header" msgid="4874760613775913787">"Maelezo muhimu, popote ulipo"</string> <string name="widget_education_content" msgid="1731667670753497052">"Ili upate maelezo bila kufungua programu, unaweza kuweka wijeti kwenye skrini yako ya kwanza"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Gusa ili ubadilishe mipangilio ya wijeti"</string> diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index 8d9b2cc06a..2742361cd7 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"தனிப்பட்டவை"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"பணி"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"உரையாடல்கள்"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"குறிப்பெடுத்தல்"</string> <string name="widget_education_header" msgid="4874760613775913787">"விரல்நுனியில் பயனுள்ள தகவல்களைப் பெறுங்கள்"</string> <string name="widget_education_content" msgid="1731667670753497052">"முகப்புத் திரையில் விட்ஜெட்டுகளைச் சேர்த்து ஆப்ஸைத் திறக்காமலேயே தகவல்களைப் பெறலாம்"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"விட்ஜெட் அமைப்புகளை மாற்றத் தட்டவும்"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"ஃபோல்டர்: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> அல்லது அதற்கு அதிகமான ஃபைல்கள்"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"வால்பேப்பர்கள்"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"வால்பேப்பர் & ஸ்டைல்"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"முகப்புத் திரையில் மாற்று"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"முகப்பு அமைப்புகள்"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"உங்கள் நிர்வாகி முடக்கியுள்ளார்"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"முகப்புத் திரை சுழற்சியை அனுமதித்தல்"</string> diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 08e633d0f2..bafa95519d 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -27,7 +27,7 @@ <string name="safemode_widget_error" msgid="4863470563535682004">"సురక్షిత మోడ్లో విడ్జెట్లు నిలిపివేయబడ్డాయి"</string> <string name="shortcut_not_available" msgid="2536503539825726397">"షార్ట్కట్ అందుబాటులో లేదు"</string> <string name="home_screen" msgid="5629429142036709174">"మొదటి ట్యాబ్"</string> - <string name="recent_task_option_split_screen" msgid="6690461455618725183">"స్ప్లిట్ స్క్రీన్"</string> + <string name="recent_task_option_split_screen" msgid="6690461455618725183">"స్క్రీన్ను విభజించు"</string> <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s కోసం యాప్ సమాచారం"</string> <string name="save_app_pair" msgid="5647523853662686243">"యాప్ పెయిర్ను సేవ్ చేయండి"</string> <string name="long_press_widget_to_add" msgid="3587712543577675817">"విడ్జెట్ను తరలించడానికి తాకి & నొక్కి ఉంచండి."</string> @@ -35,7 +35,7 @@ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string> <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d వెడల్పు X %2$d ఎత్తు"</string> <string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> విడ్జెట్"</string> - <string name="add_item_request_drag_hint" msgid="8730547755622776606">"విడ్జెట్ను మొదటి స్క్రీన్లో తిప్పడానికి దాన్ని తాకి, & నొక్కి పట్టుకోండి"</string> + <string name="add_item_request_drag_hint" msgid="8730547755622776606">"విడ్జెట్ను మొదటి స్క్రీన్ చుట్టూ తిప్పడానికి దాన్ని తాకి, & నొక్కి ఉంచండి"</string> <string name="add_to_home_screen" msgid="9168649446635919791">"మొదటి స్క్రీన్కు జోడించండి"</string> <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"మొదటి స్క్రీన్కు <xliff:g id="WIDGET_NAME">%1$s</xliff:g> విడ్జెట్ జోడించబడింది"</string> <string name="suggested_widgets_header_title" msgid="1844314680798145222">"సూచనలు"</string> @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"వ్యక్తిగత గ్యాడ్జెట్స్"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ఆఫీస్"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"సంభాషణలు"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"నోట్-టేకింగ్"</string> <string name="widget_education_header" msgid="4874760613775913787">"మీ చేతివేళ్ల మీద ఉపయోగకరమైన సమాచారం"</string> <string name="widget_education_content" msgid="1731667670753497052">"యాప్లను తెరవకుండా సమాచారాన్ని పొందడానికి, మీరు మీ మొదటి స్క్రీన్కు విడ్జెట్లను జోడించవచ్చు"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"విడ్జెట్ సెట్టింగ్లను మార్చడానికి ట్యాప్ చేయండి"</string> @@ -168,7 +167,7 @@ <string name="work_apps_paused_telephony_unavailable_body" msgid="8358872357502756790">"మీ వర్క్ యాప్ల నుండి మీకు ఫోన్ కాల్స్, టెక్స్ట్ మెసేజ్లు, లేదా నోటిఫికేషన్లు అందవు"</string> <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"వర్క్ యాప్లకు బ్యాడ్జ్ ఉంటుంది, అవి మీ IT అడ్మిన్కు కనిపిస్తాయి"</string> <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"అర్థమైంది"</string> - <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"వర్క్ యాప్లను పాజ్ చేయండి"</string> + <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"వర్క్ యాప్లను పాజ్ చేయి"</string> <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"పాజ్ నుండి తీసివేయండి"</string> <string name="developer_options_filter_hint" msgid="5896817443635989056">"ఫిల్టర్ చేయి"</string> <string name="search_pref_screen_title" msgid="3258959643336315962">"మీ ఫోన్లో సెర్చ్ చేయండి"</string> diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 4b64993bf3..c75c5326ae 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -27,7 +27,7 @@ <string name="safemode_widget_error" msgid="4863470563535682004">"มีการปิดใช้งานวิดเจ็ตในเซฟโหมด"</string> <string name="shortcut_not_available" msgid="2536503539825726397">"ทางลัดไม่พร้อมใช้งาน"</string> <string name="home_screen" msgid="5629429142036709174">"หน้าแรก"</string> - <string name="recent_task_option_split_screen" msgid="6690461455618725183">"แยกหน้าจอ"</string> + <string name="recent_task_option_split_screen" msgid="6690461455618725183">"แบ่งหน้าจอ"</string> <string name="split_app_info_accessibility" msgid="5475288491241414932">"ข้อมูลแอปสำหรับ %1$s"</string> <string name="save_app_pair" msgid="5647523853662686243">"บันทึกคู่แอป"</string> <string name="long_press_widget_to_add" msgid="3587712543577675817">"แตะค้างไว้เพื่อย้ายวิดเจ็ต"</string> @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ส่วนตัว"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"งาน"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"การสนทนา"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"การจดบันทึก"</string> <string name="widget_education_header" msgid="4874760613775913787">"เข้าถึงข้อมูลที่เป็นประโยชน์ได้จากปลายนิ้ว"</string> <string name="widget_education_content" msgid="1731667670753497052">"หากต้องการรับข้อมูลโดยไม่เปิดแอป ให้เพิ่มวิดเจ็ตลงในหน้าจอหลัก"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"แตะเพื่อเปลี่ยนการตั้งค่าวิดเจ็ต"</string> @@ -162,7 +161,7 @@ <string name="work_profile_toggle_label" msgid="3081029915775481146">"โปรไฟล์งาน"</string> <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"แอปงานจะติดป้ายไว้และผู้ดูแลระบบไอทีจะมองเห็น"</string> <string name="work_profile_edu_accept" msgid="6069788082535149071">"รับทราบ"</string> - <string name="work_apps_paused_title" msgid="3040901117349444598">"แอปงานหยุดชั่วคราว"</string> + <string name="work_apps_paused_title" msgid="3040901117349444598">"แอปงานปิดอยู่"</string> <string name="work_apps_paused_info_body" msgid="1687828929959237477">"คุณจะไม่ได้รับการแจ้งเตือนจากแอปงาน"</string> <string name="work_apps_paused_body" msgid="261634750995824906">"แอปงานจะส่งการแจ้งเตือน ใช้แบตเตอรี่ หรือเข้าถึงตำแหน่งของคุณไม่ได้"</string> <string name="work_apps_paused_telephony_unavailable_body" msgid="8358872357502756790">"คุณจะไม่ได้รับสายโทรเข้า, SMS หรือการแจ้งเตือนจากแอปงาน"</string> diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 61f92273aa..b91d67e8be 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabaho"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Mga Pag-uusap"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Pagtatala"</string> <string name="widget_education_header" msgid="4874760613775913787">"Abot-kamay na mahalagang impormasyon"</string> <string name="widget_education_content" msgid="1731667670753497052">"Para makakuha ng impormasyon nang hindi nagbubukas ng mga app, puwede kang magdagdag ng mga widget sa iyong home screen"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"I-tap para baguhin ang mga setting ng widget"</string> diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index ef28f18dfd..ed0e6cfa50 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Kişisel"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"İş"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Görüşmeler"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Not alma"</string> <string name="widget_education_header" msgid="4874760613775913787">"Faydalı bilgiler parmaklarınızın ucunda"</string> <string name="widget_education_content" msgid="1731667670753497052">"Uygulama açmadan bilgi almak için ana ekranınıza widget ekleyebilirsiniz"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Widget ayarlarını değiştirmek için dokunun"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> veya daha fazla öğe"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Duvar Kağıtları"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Duvar kağıdı ve stil"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Ana ekranı düzenleyin"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Ana ekran ayarları"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Yöneticiniz tarafından devre dışı bırakıldı"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Ana ekranı döndürmeye izin ver"</string> diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 822936be1b..8c9524aae9 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Особисті"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Робочі"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Розмови"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Створення нотаток"</string> <string name="widget_education_header" msgid="4874760613775913787">"Корисна інформація завжди під рукою"</string> <string name="widget_education_content" msgid="1731667670753497052">"Щоб отримувати інформацію, не відкриваючи додатки, ви можете додати на головний екран віджети"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Натисніть, щоб змінити налаштування віджета"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\", елементів: <xliff:g id="SIZE">%2$d</xliff:g> або більше"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Фонові малюнки"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Оформлення і стиль"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Редагувати головний екран"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Налаштування головного екрана"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Вимкнув адміністратор"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Дозволити обертання головного екрана"</string> diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index 1fffcfd1a1..5b10dd21ee 100644 --- a/res/values-ur/strings.xml +++ b/res/values-ur/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ذاتی"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"دفتری ویجیٹس"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"گفتگوئیں"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"نوٹ لکھنا"</string> <string name="widget_education_header" msgid="4874760613775913787">"مفید معلومات کو آسانی سے حاصل کریں"</string> <string name="widget_education_content" msgid="1731667670753497052">"ایپس کو کھولے بغیر معلومات حاصل کرنے کے لیے آپ اپنی ہوم اسکرین پر ویجیٹس شامل کر سکتے ہیں"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ویجیٹ ترتیبات تبدیل کرنے کے لیے تھپتھپائیں"</string> diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index db381caa04..81ad4dce90 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Shaxsiy"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Ish"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Suhbatlar"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Qayd olish"</string> <string name="widget_education_header" msgid="4874760613775913787">"Barcha kerakli axborot doim yoningizda"</string> <string name="widget_education_content" msgid="1731667670753497052">"Kerakli ilovalarni ochmasdan turib ulardan axborot olish uchun vidjetlarni bosh ekranga chiqaring"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Vidjet sozlamalarini oʻzgartirish uchun bosing"</string> diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index b73bc615da..ba7b0ce2cf 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Cá nhân"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Công việc"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Cuộc trò chuyện"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Ghi chú"</string> <string name="widget_education_header" msgid="4874760613775913787">"Thông tin hữu ích ngay trong tầm tay bạn"</string> <string name="widget_education_content" msgid="1731667670753497052">"Để nhận thông tin mà không cần mở các ứng dụng, bạn có thể thêm tiện ích vào màn hình chính"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Nhấn để thay đổi chế độ cài đặt tiện ích"</string> @@ -101,7 +100,8 @@ <string name="folder_name_format_overflow" msgid="4270108890534995199">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> mục trở lên"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"Hình nền"</string> <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Hình nền và phong cách"</string> - <string name="edit_home_screen" msgid="8947858375782098427">"Chỉnh sửa Màn hình chính"</string> + <!-- no translation found for edit_home_screen (8947858375782098427) --> + <skip /> <string name="settings_button_text" msgid="8873672322605444408">"Cài đặt màn hình chính"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Bị tắt bởi quản trị viên của bạn"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"Cho phép xoay màn hình chính"</string> diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 20c8ea9694..415f5fa93d 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"个人"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"工作"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"对话"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"记事"</string> <string name="widget_education_header" msgid="4874760613775913787">"实用信息触手可及"</string> <string name="widget_education_content" msgid="1731667670753497052">"要想不打开应用就能获取信息,您可以将相应微件添加到主屏幕"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"点按即可更改微件设置"</string> @@ -100,7 +99,7 @@ <string name="folder_name_format_exact" msgid="8626242716117004803">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 个项目"</string> <string name="folder_name_format_overflow" msgid="4270108890534995199">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 个或更多项目"</string> <string name="wallpaper_button_text" msgid="8404103075899945851">"壁纸"</string> - <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"壁纸与个性化"</string> + <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"壁纸和样式"</string> <string name="edit_home_screen" msgid="8947858375782098427">"修改主屏幕"</string> <string name="settings_button_text" msgid="8873672322605444408">"主屏幕设置"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已被您的管理员停用"</string> diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index a01b2634b1..14291697d2 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"個人"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"工作"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"對話"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"做筆記"</string> <string name="widget_education_header" msgid="4874760613775913787">"實用資訊,唾手可得"</string> <string name="widget_education_content" msgid="1731667670753497052">"只要將小工具新增至主畫面,就可以直接查看資料,無需開啟應用程式"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"輕按即可變更小工具設定"</string> @@ -103,7 +102,7 @@ <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"桌布和樣式"</string> <string name="edit_home_screen" msgid="8947858375782098427">"編輯主畫面"</string> <string name="settings_button_text" msgid="8873672322605444408">"主畫面設定"</string> - <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由你的管理員停用"</string> + <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由您的管理員停用"</string> <string name="allow_rotation_title" msgid="7222049633713050106">"允許旋轉主畫面"</string> <string name="allow_rotation_desc" msgid="8662546029078692509">"當手機旋轉時"</string> <string name="notification_dots_title" msgid="9062440428204120317">"通知圓點"</string> @@ -120,12 +119,12 @@ <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string> <string name="abandoned_search" msgid="891119232568284442">"搜尋"</string> <string name="abandoned_promises_title" msgid="7096178467971716750">"尚未安裝這個應用程式"</string> - <string name="abandoned_promise_explanation" msgid="3990027586878167529">"你尚未安裝這個圖示代表的應用程式。你可以移除這個圖示,也可以搜尋該應用程式並手動安裝。"</string> + <string name="abandoned_promise_explanation" msgid="3990027586878167529">"您尚未安裝這個圖示代表的應用程式。您可以移除這個圖示,也可以搜尋該應用程式並手動安裝。"</string> <string name="app_installing_title" msgid="5864044122733792085">"正在安裝「<xliff:g id="NAME">%1$s</xliff:g>」(已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string> <string name="app_downloading_title" msgid="8336702962104482644">"正在下載 <xliff:g id="NAME">%1$s</xliff:g>,已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>"</string> <string name="app_waiting_download_title" msgid="7053938513995617849">"正在等待安裝 <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="dialog_update_title" msgid="114234265740994042">"必須更新應用程式"</string> - <string name="dialog_update_message" msgid="4176784553982226114">"你尚未更新這個圖示代表的應用程式。你可以手動更新以重新啟用此快速鍵,或者移除圖示。"</string> + <string name="dialog_update_message" msgid="4176784553982226114">"您尚未更新這個圖示代表的應用程式。您可以手動更新以重新啟用此快速鍵,或者移除圖示。"</string> <string name="dialog_update" msgid="2178028071796141234">"更新"</string> <string name="dialog_remove" msgid="6510806469849709407">"移除"</string> <string name="widgets_list" msgid="796804551140113767">"小工具清單"</string> @@ -160,13 +159,13 @@ <string name="all_apps_personal_tab" msgid="4190252696685155002">"個人"</string> <string name="all_apps_work_tab" msgid="4884822796154055118">"工作"</string> <string name="work_profile_toggle_label" msgid="3081029915775481146">"工作設定檔"</string> - <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"工作應用程式均加有標誌。你的 IT 管理員可以看到這些應用程式"</string> + <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"工作應用程式均加有標誌。您的 IT 管理員可以看到這些應用程式"</string> <string name="work_profile_edu_accept" msgid="6069788082535149071">"知道了"</string> <string name="work_apps_paused_title" msgid="3040901117349444598">"已暫停工作應用程式"</string> - <string name="work_apps_paused_info_body" msgid="1687828929959237477">"你不會收到工作應用程式的通知"</string> - <string name="work_apps_paused_body" msgid="261634750995824906">"工作應用程式無法向你傳送通知、使用電池電量或存取你的位置"</string> - <string name="work_apps_paused_telephony_unavailable_body" msgid="8358872357502756790">"你不會收到工作應用程式的來電、短訊或通知"</string> - <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"工作應用程式均加有標誌。你的 IT 管理員可以看到這些應用程式"</string> + <string name="work_apps_paused_info_body" msgid="1687828929959237477">"您不會收到工作應用程式的通知"</string> + <string name="work_apps_paused_body" msgid="261634750995824906">"工作應用程式無法向您傳送通知、使用電池電量或存取您的位置"</string> + <string name="work_apps_paused_telephony_unavailable_body" msgid="8358872357502756790">"您不會收到工作應用程式的來電、短訊或通知"</string> + <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"工作應用程式均加有標誌。您的 IT 管理員可以看到這些應用程式"</string> <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"知道了"</string> <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"暫停工作應用程式"</string> <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"取消暫停"</string> diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 69d1d90417..7f949e8e41 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"個人"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"工作"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"對話"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"做筆記"</string> <string name="widget_education_header" msgid="4874760613775913787">"實用資訊隨手可得"</string> <string name="widget_education_content" msgid="1731667670753497052">"只要將小工具新增到主畫面,就可以直接查看資訊,不必開啟應用程式"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"輕觸即可變更小工具設定"</string> diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index d9ed3f03af..6b457d116e 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -50,7 +50,6 @@ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Okwabantu siqu"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Umsebenzi"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Izingxoxo"</string> - <string name="widget_category_note_taking" msgid="3469689394504266039">"Ukuthatha amanothi"</string> <string name="widget_education_header" msgid="4874760613775913787">"Ulwazi oluwusizo phambi nje kwakho"</string> <string name="widget_education_content" msgid="1731667670753497052">"Ukuze uthole ulwazi ngaphandle kokuvula ama-app, ungakwazi ukwengeza amawijethi kusikrini sakho sasekhaya"</string> <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Thepha ukuze ushintshe amasethingi ewijethi"</string> @@ -60,7 +59,7 @@ <string name="all_apps_loading_message" msgid="5813968043155271636">"Ilayisha izinhlelo zokusebenza..."</string> <string name="all_apps_no_search_results" msgid="3200346862396363786">"Azikho izinhlelo zokusebenza ezitholiwe ezifana ne-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string> <string name="label_application" msgid="8531721983832654978">"Uhlelo lokusebenza"</string> - <string name="all_apps_label" msgid="5015784846527570951">"Wonke ama-app"</string> + <string name="all_apps_label" msgid="5015784846527570951">"Zonke izinhlelo zokusebenza"</string> <string name="notifications_header" msgid="1404149926117359025">"Izaziso"</string> <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Thinta uphinde ubambe ukuze uhambise isinqamuleli."</string> <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Thepha kabili uphinde ubambe ukuze uhambise isinqamuleli noma usebenzise izenzo ezingokwezifiso."</string> diff --git a/res/values/attrs.xml b/res/values/attrs.xml index e4650b28b3..f046eca3dc 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -36,7 +36,6 @@ <attr name="workspaceShadowColor" format="color" /> <attr name="workspaceAmbientShadowColor" format="color" /> <attr name="workspaceKeyShadowColor" format="color" /> - <attr name="workspaceStatusBarScrim" format="reference" /> <attr name="widgetsTheme" format="reference" /> <attr name="iconOnlyShortcutColor" format="color" /> <attr name="eduHalfSheetBGColor" format="color" /> @@ -86,6 +85,8 @@ <enum name="taskbar" value="5" /> <enum name="search_result_tall" value="6" /> <enum name="search_result_small" value="7" /> + <enum name="prediction_row" value="8" /> + <enum name="search_result_app_row" value="9" /> </attr> <attr name="centerVertically" format="boolean" /> </declare-styleable> @@ -202,9 +203,24 @@ <attr name="demoModeLayoutId" format="reference" /> <attr name="isScalable" format="boolean" /> <attr name="devicePaddingId" format="reference" /> + <!-- File that contains the specs for the workspace. Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled --> <attr name="workspaceSpecsId" format="reference" /> + <attr name="workspaceSpecsTwoPanelId" format="reference" /> + <!-- File that contains the specs for all apps. + Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled --> + <attr name="allAppsSpecsId" format="reference" /> + <attr name="allAppsSpecsTwoPanelId" format="reference" /> + <!-- File that contains the specs for the workspace. + Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled --> + <attr name="folderSpecsId" format="reference" /> + <attr name="folderSpecsTwoPanelId" format="reference" /> + <!-- File that contains the specs for hotseat bar. + Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled --> + <attr name="hotseatSpecsId" format="reference" /> + <attr name="hotseatSpecsTwoPanelId" format="reference" /> + <!-- By default all categories are enabled --> <attr name="deviceCategory" format="integer"> <!-- Enable on phone only --> @@ -244,7 +260,7 @@ </declare-styleable> <!-- Responsive grids attributes --> - <declare-styleable name="WorkspaceSpec"> + <declare-styleable name="ResponsiveSpec"> <attr name="specType" format="integer"> <enum name="height" value="0" /> <enum name="width" value="1" /> @@ -252,10 +268,32 @@ <attr name="maxAvailableSize" format="dimension" /> </declare-styleable> - <declare-styleable name="SpecSize"> + <declare-styleable name="WorkspaceSpec"> + <attr name="specType" /> + <attr name="maxAvailableSize" /> + </declare-styleable> + + <declare-styleable name="FolderSpec"> + <attr name="specType" /> + <attr name="maxAvailableSize" /> + </declare-styleable> + + <declare-styleable name="AllAppsSpec"> + <attr name="specType" /> + <attr name="maxAvailableSize" /> + </declare-styleable> + + <declare-styleable name="HotseatSpec"> + <attr name="specType" /> + <attr name="maxAvailableSize" /> + </declare-styleable> + + <declare-styleable name="SizeSpec"> <attr name="fixedSize" format="dimension" /> <attr name="ofAvailableSpace" format="float" /> <attr name="ofRemainderSpace" format="float" /> + <attr name="matchWorkspace" format="boolean" /> + <attr name="maxSize" format="dimension" /> </declare-styleable> <declare-styleable name="ProfileDisplayOption"> @@ -564,4 +602,9 @@ <!-- The icon drawable of a widget category. --> <attr name="sectionDrawable" format="reference" /> </declare-styleable> + + <declare-styleable name="ArrowTipView"> + <attr name="arrowTipBackground" format="color" /> + <attr name="arrowTipTextColor" format="color" /> + </declare-styleable> </resources> diff --git a/res/values/config.xml b/res/values/config.xml index 5a6698b80e..8f9731c534 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -84,6 +84,8 @@ <string name="window_manager_proxy_class" translatable="false"></string> <string name="secondary_display_predictions_class" translatable="false"></string> <string name="widget_holder_factory_class" translatable="false"></string> + <string name="taskbar_search_session_controller_class" translatable="false"></string> + <string name="taskbar_model_callbacks_factory_class" translatable="false"></string> <!-- View ID to use for QSB widget --> <item type="id" name="qsb_widget" /> @@ -102,8 +104,6 @@ <!-- Default packages --> <string name="wallpaper_picker_package" translatable="false"></string> - <string name="custom_activity_picker" translatable="false"> - com.android.customization.picker.CustomizationPickerActivity</string> <string name="local_colors_extraction_class" translatable="false"></string> <string name="search_session_manager_class" translatable="false"></string> @@ -217,4 +217,28 @@ <!-- Whether the floating rotation button should be on the left/right in the device's natural orientation --> <bool name="floating_rotation_button_position_left">true</bool> + + <!-- Mapping of visual icon size to XML value http://b/235886078 --> + <dimen name="iconSize48dp">52dp</dimen> + <dimen name="iconSize50dp">55dp</dimen> + <dimen name="iconSize52dp">57dp</dimen> + <dimen name="iconSize54dp">59dp</dimen> + <dimen name="iconSize56dp">61dp</dimen> + <dimen name="iconSize58dp">63dp</dimen> + <dimen name="iconSize60dp">66dp</dimen> + <dimen name="iconSize66dp">72dp</dimen> + <dimen name="iconSize72dp">79dp</dimen> + + <!-- Icon size steps in dp --> + <integer-array name="icon_size_steps"> + <item>@dimen/iconSize48dp</item> + <item>@dimen/iconSize50dp</item> + <item>@dimen/iconSize52dp</item> + <item>@dimen/iconSize54dp</item> + <item>@dimen/iconSize56dp</item> + <item>@dimen/iconSize58dp</item> + <item>@dimen/iconSize60dp</item> + <item>@dimen/iconSize66dp</item> + <item>@dimen/iconSize72dp</item> + </integer-array> </resources> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 786088ef6e..1079e0000d 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -411,6 +411,7 @@ <dimen name="split_instructions_elevation">1dp</dimen> <dimen name="split_instructions_horizontal_padding">24dp</dimen> <dimen name="split_instructions_vertical_padding">12dp</dimen> + <dimen name="split_instructions_drawable_padding">10dp</dimen> <dimen name="split_instructions_bottom_margin_phone_landscape">24dp</dimen> <dimen name="split_instructions_bottom_margin_phone_portrait">60dp</dimen> @@ -438,4 +439,8 @@ <!-- Folder spaces --> <dimen name="folder_top_padding_default">24dp</dimen> <dimen name="folder_footer_horiz_padding">20dp</dimen> + + <!-- Default Ime height. Used only for logging purposes. + Assume this is default keyboard height in EN locale in case the keyboard height is not known when queried.--> + <dimen name="default_ime_height">300dp</dimen> </resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index c2eb3735af..1b46b4dba8 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -250,8 +250,6 @@ <!-- Strings for the customization mode --> <!-- Text for wallpaper change button [CHAR LIMIT=30]--> - <string name="wallpaper_button_text">Wallpapers</string> - <!-- Text for wallpaper change button [CHAR LIMIT=30]--> <string name="styles_wallpaper_button_text">Wallpaper & style</string> <!-- Text for edit home screen button [CHAR LIMIT=30]--> <string name="edit_home_screen">Edit Home Screen</string> diff --git a/res/values/styles.xml b/res/values/styles.xml index c41f0e86e9..4cb64140f8 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -32,7 +32,8 @@ <style name="LauncherTheme" parent="@style/BaseLauncherTheme"> <item name="android:textColorSecondary">#DE000000</item> <item name="allAppsScrimColor">?attr/materialColorSurfaceDim</item> - <item name="allappsHeaderProtectionColor">@color/popup_color_tertiary_light</item> + <item name="allappsHeaderProtectionColor"> + @color/material_color_surface_container_highest</item> <item name="allAppsNavBarScrimColor">#66FFFFFF</item> <item name="popupColorPrimary">@color/popup_color_primary_light</item> <item name="popupColorSecondary">@color/popup_color_secondary_light</item> @@ -50,7 +51,6 @@ <item name="workspaceShadowColor">#B0000000</item> <item name="workspaceAmbientShadowColor">#40000000</item> <item name="workspaceKeyShadowColor">#89000000</item> - <item name="workspaceStatusBarScrim">@drawable/workspace_bg</item> <item name="widgetsTheme">@style/WidgetContainerTheme</item> <item name="folderPaginationColor">@color/folder_pagination_color_light</item> <item name="folderPreviewColor">@color/folder_preview_light</item> @@ -137,7 +137,6 @@ <item name="workspaceAmbientShadowColor">@android:color/transparent</item> <item name="workspaceKeyShadowColor">@android:color/transparent</item> <item name="isWorkspaceDarkText">true</item> - <item name="workspaceStatusBarScrim">@null</item> <item name="workspaceAccentColor">@color/workspace_accent_color_dark</item> <item name="dropTargetHoverTextColor">@color/drop_target_hover_text_color_dark</item> <item name="dropTargetHoverButtonColor">@color/drop_target_hover_button_color_dark</item> @@ -151,6 +150,7 @@ <item name="android:colorControlHighlight">#19FFFFFF</item> <item name="android:colorPrimary">#FF212121</item> <item name="allAppsScrimColor">?attr/materialColorSurfaceDim</item> + <item name="allappsHeaderProtectionColor">@color/material_color_surface_container_low</item> <item name="allAppsNavBarScrimColor">#80000000</item> <item name="popupColorPrimary">@color/popup_color_primary_dark</item> <item name="popupColorSecondary">@color/popup_color_secondary_dark</item> @@ -188,7 +188,6 @@ <item name="workspaceAmbientShadowColor">@android:color/transparent</item> <item name="workspaceKeyShadowColor">@android:color/transparent</item> <item name="isWorkspaceDarkText">true</item> - <item name="workspaceStatusBarScrim">@null</item> <item name="workspaceAccentColor">@color/workspace_accent_color_dark</item> <item name="dropTargetHoverTextColor">@color/drop_target_hover_text_color_dark</item> <item name="dropTargetHoverButtonColor">@color/drop_target_hover_button_color_dark</item> @@ -426,4 +425,8 @@ <item name="horizontalPadding">16dp</item> </style> + <style name="ArrowTipStyle"> + <item name="arrowTipBackground">@color/arrow_tip_view_bg</item> + <item name="arrowTipTextColor">@color/arrow_tip_view_content</item> + </style> </resources> diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index 31f9bfea4a..b845c88d23 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -133,6 +133,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public static final int TYPE_TASKBAR_OVERLAYS = TYPE_TASKBAR_ALL_APPS | TYPE_TASKBAR_EDUCATION_DIALOG; + public static final int TYPE_ALL_EXCEPT_ON_BOARD_POPUP = TYPE_ALL & ~TYPE_ON_BOARD_POPUP; + protected boolean mIsOpen; public AbstractFloatingView(Context context, AttributeSet attrs) { diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index ede7e2f950..c7cdfa8c69 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -17,6 +17,8 @@ package com.android.launcher3; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; +import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; +import static com.android.launcher3.provider.LauncherDbUtils.itemIdMatch; import android.content.ComponentName; import android.content.ContentValues; @@ -30,7 +32,6 @@ import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.content.res.XmlResourceParser; import android.database.sqlite.SQLiteDatabase; -import android.net.Uri; import android.os.Bundle; import android.os.Process; import android.text.TextUtils; @@ -44,7 +45,6 @@ import androidx.annotation.StringRes; import androidx.annotation.WorkerThread; import androidx.annotation.XmlRes; -import com.android.launcher3.LauncherProvider.SqlArguments; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; @@ -619,9 +619,7 @@ public class AutoInstallsLayout { // failed to add, and less than 2 were actually added if (folderItems.size() < 2) { // Delete the folder - Uri uri = Favorites.getContentUri(folderId); - SqlArguments args = new SqlArguments(uri, null, null); - mDb.delete(args.table, args.where, args.args); + mDb.delete(TABLE_NAME, itemIdMatch(folderId), null); addedId = -1; // If we have a single item, promote it to where the folder @@ -634,7 +632,7 @@ public class AutoInstallsLayout { copyInteger(myValues, childValues, Favorites.CELLY); addedId = folderItems.get(0); - mDb.update(Favorites.TABLE_NAME, childValues, + mDb.update(TABLE_NAME, childValues, Favorites._ID + "=" + addedId, null); } } diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java index 8876a1ba34..808cf70a50 100644 --- a/src/com/android/launcher3/BaseDraggingActivity.java +++ b/src/com/android/launcher3/BaseDraggingActivity.java @@ -17,12 +17,7 @@ package com.android.launcher3; import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION; -import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; -import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR; -import android.app.WallpaperColors; -import android.app.WallpaperManager; -import android.app.WallpaperManager.OnColorsChangedListener; import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; @@ -32,6 +27,7 @@ import android.view.ActionMode; import android.view.Display; import android.view.View; +import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -41,9 +37,11 @@ import com.android.launcher3.util.ActivityOptionsWrapper; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener; import com.android.launcher3.util.DisplayController.Info; +import com.android.launcher3.util.OnColorHintListener; import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.Themes; import com.android.launcher3.util.TraceHelper; +import com.android.launcher3.util.WallpaperColorHints; import com.android.launcher3.util.WindowBounds; /** @@ -51,7 +49,7 @@ import com.android.launcher3.util.WindowBounds; */ @SuppressWarnings("NewApi") public abstract class BaseDraggingActivity extends BaseActivity - implements OnColorsChangedListener, DisplayInfoChangeListener { + implements OnColorHintListener, DisplayInfoChangeListener { private static final String TAG = "BaseDraggingActivity"; @@ -63,8 +61,7 @@ public abstract class BaseDraggingActivity extends BaseActivity protected boolean mIsSafeModeEnabled; private Runnable mOnStartCallback; - private RunnableList mOnResumeCallbacks = new RunnableList(); - + private final RunnableList mOnResumeCallbacks = new RunnableList(); private int mThemeRes = R.style.AppTheme; @Override @@ -76,10 +73,7 @@ public abstract class BaseDraggingActivity extends BaseActivity DisplayController.INSTANCE.get(this).addChangeListener(this); // Update theme - if (Utilities.ATLEAST_P) { - THREAD_POOL_EXECUTOR.execute(() -> getSystemService(WallpaperManager.class) - .addOnColorsChangedListener(this, MAIN_EXECUTOR.getHandler())); - } + WallpaperColorHints.get(this).registerOnColorHintsChangedListener(this); int themeRes = Themes.getActivityThemeRes(this); if (themeRes != mThemeRes) { mThemeRes = themeRes; @@ -97,8 +91,9 @@ public abstract class BaseDraggingActivity extends BaseActivity mOnResumeCallbacks.add(callback); } + @MainThread @Override - public void onColorsChanged(WallpaperColors wallpaperColors, int which) { + public void onColorHintsChanged(int colorHints) { updateTheme(); } @@ -175,10 +170,8 @@ public abstract class BaseDraggingActivity extends BaseActivity @Override protected void onDestroy() { super.onDestroy(); - if (Utilities.ATLEAST_P) { - getSystemService(WallpaperManager.class).removeOnColorsChangedListener(this); - } DisplayController.INSTANCE.get(this).removeChangeListener(this); + WallpaperColorHints.get(this).unregisterOnColorsChangedListener(this); } public void runOnceOnStart(Runnable action) { diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index f920d7507c..a539148a29 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -16,6 +16,7 @@ package com.android.launcher3; +import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES; import static com.android.launcher3.config.FeatureFlags.ENABLE_DOWNLOAD_APP_UX_V2; import static com.android.launcher3.config.FeatureFlags.ENABLE_ICON_LABEL_AUTO_SCALING; import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon; @@ -94,11 +95,13 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, IconLabelDotView, DraggableView, Reorderable { private static final int DISPLAY_WORKSPACE = 0; - private static final int DISPLAY_ALL_APPS = 1; + public static final int DISPLAY_ALL_APPS = 1; private static final int DISPLAY_FOLDER = 2; protected static final int DISPLAY_TASKBAR = 5; - private static final int DISPLAY_SEARCH_RESULT = 6; - private static final int DISPLAY_SEARCH_RESULT_SMALL = 7; + public static final int DISPLAY_SEARCH_RESULT = 6; + public static final int DISPLAY_SEARCH_RESULT_SMALL = 7; + public static final int DISPLAY_PREDICTION_ROW = 8; + public static final int DISPLAY_SEARCH_RESULT_APP_ROW = 9; private static final float MIN_LETTER_SPACING = -0.05f; private static final int MAX_SEARCH_LOOP_COUNT = 20; @@ -151,7 +154,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private final CheckLongPressHelper mLongPressHelper; - private final boolean mLayoutHorizontal; + private boolean mLayoutHorizontal; private final boolean mIsRtl; private final int mIconSize; @@ -196,6 +199,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, public BubbleTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mActivity = ActivityContext.lookupContext(context); + FastBitmapDrawable.setFlagHoverEnabled(ENABLE_CURSOR_HOVER_STATES.get()); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BubbleTextView, defStyle, 0); @@ -210,8 +214,9 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); setCompoundDrawablePadding(grid.iconDrawablePaddingPx); defaultIconSize = grid.iconSizePx; - setCenterVertically(grid.isScalableGrid); - } else if (mDisplay == DISPLAY_ALL_APPS) { + setCenterVertically(grid.iconCenterVertically); + } else if (mDisplay == DISPLAY_ALL_APPS || mDisplay == DISPLAY_PREDICTION_ROW + || mDisplay == DISPLAY_SEARCH_RESULT_APP_ROW) { setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx); setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx); defaultIconSize = grid.allAppsIconSizePx; @@ -402,7 +407,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, * Only if actual text can be displayed in two line, the {@code true} value will be effective. */ protected boolean shouldUseTwoLine() { - return (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() && mDisplay == DISPLAY_ALL_APPS) + return (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() && mDisplay == DISPLAY_ALL_APPS) || (FeatureFlags.ENABLE_TWOLINE_DEVICESEARCH.get() && mDisplay == DISPLAY_SEARCH_RESULT); } @@ -424,10 +429,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } } - /** This is used for testing to forcefully set the display to ALL_APPS */ + /** This is used for testing to forcefully set the display. */ @VisibleForTesting - public void setDisplayAllApps() { - mDisplay = DISPLAY_ALL_APPS; + public void setDisplay(int display) { + mDisplay = display; } /** @@ -631,6 +636,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } } + @VisibleForTesting + public boolean getForceHideDot() { + return mForceHideDot; + } + private boolean hasDot() { return mDotInfo != null; } @@ -660,6 +670,18 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } /** + * Sets whether the layout is horizontal. + */ + public void setLayoutHorizontal(boolean layoutHorizontal) { + if (mLayoutHorizontal == layoutHorizontal) { + return; + } + + mLayoutHorizontal = layoutHorizontal; + applyCompoundDrawables(getIconOrTransparentColor()); + } + + /** * Sets whether to vertically center the content. */ public void setCenterVertically(boolean centerVertically) { @@ -985,10 +1007,14 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, if (!mIsIconVisible) { resetIconScale(); } - Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT); + Drawable icon = getIconOrTransparentColor(); applyCompoundDrawables(icon); } + private Drawable getIconOrTransparentColor() { + return mIsIconVisible ? mIcon : new ColorDrawable(Color.TRANSPARENT); + } + /** Sets the icon visual state to disabled or not. */ public void setIconDisabled(boolean isDisabled) { if (mIcon != null) { @@ -1080,8 +1106,13 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } public boolean isDisplaySearchResult() { - return mDisplay == DISPLAY_SEARCH_RESULT || - mDisplay == DISPLAY_SEARCH_RESULT_SMALL; + return mDisplay == DISPLAY_SEARCH_RESULT + || mDisplay == DISPLAY_SEARCH_RESULT_SMALL + || mDisplay == DISPLAY_SEARCH_RESULT_APP_ROW; + } + + public int getIconDisplay() { + return mDisplay; } @Override diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 108e557797..3d715e59a8 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -35,7 +35,7 @@ import android.widget.TextView; import androidx.annotation.VisibleForTesting; -import com.android.launcher3.anim.Interpolators; +import com.android.app.animation.Interpolators; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; @@ -258,7 +258,7 @@ public abstract class ButtonDropTarget extends TextView dragLayer.animateView(d.dragView, to, scale, 0.1f, 0.1f, DRAG_VIEW_DROP_DURATION, - Interpolators.DEACCEL_2, onAnimationEndRunnable, + Interpolators.DECELERATE_2, onAnimationEndRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null); } diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 61b27486f8..08e5def70d 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -18,8 +18,8 @@ package com.android.launcher3; import static android.animation.ValueAnimator.areAnimatorsEnabled; +import static com.android.app.animation.Interpolators.DECELERATE_1_5; import static com.android.launcher3.LauncherState.EDIT_MODE; -import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5; import static com.android.launcher3.dragndrop.DraggableView.DRAGGABLE_ICON; import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR; import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_REORDER_BOUNCE_OFFSET; @@ -60,9 +60,9 @@ import androidx.annotation.IntDef; import androidx.core.graphics.ColorUtils; import androidx.core.view.ViewCompat; +import com.android.app.animation.Interpolators; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.celllayout.CellLayoutLayoutParams; import com.android.launcher3.celllayout.CellPosMapper.CellPos; import com.android.launcher3.celllayout.ReorderAlgorithm; @@ -245,6 +245,7 @@ public class CellLayout extends ViewGroup { // the user where a dragged item will land when dropped. setWillNotDraw(false); setClipToPadding(false); + setClipChildren(false); mActivity = ActivityContext.lookupContext(context); DeviceProfile deviceProfile = mActivity.getDeviceProfile(); @@ -272,7 +273,7 @@ public class CellLayout extends ViewGroup { mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * deviceProfile.iconSizePx); // Initialize the data structures used for the drag visualization. - mEaseOutInterpolator = Interpolators.DEACCEL_2_5; // Quint ease out + mEaseOutInterpolator = Interpolators.DECELERATE_QUINT; // Quint ease out mDragCell[0] = mDragCell[1] = -1; mDragCellSpan[0] = mDragCellSpan[1] = -1; for (int i = 0; i < mDragOutlines.length; i++) { @@ -382,8 +383,7 @@ public class CellLayout extends ViewGroup { private void resetCellSizeInternal(DeviceProfile deviceProfile) { switch (mContainerType) { case FOLDER: - mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx, - deviceProfile.folderCellLayoutBorderSpacePx); + mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx); break; case HOTSEAT: mBorderSpace = new Point(deviceProfile.hotseatBorderSpace, @@ -1606,7 +1606,7 @@ public class CellLayout extends ViewGroup { ValueAnimator va = ObjectAnimator.ofFloat(this, ANIMATION_PROGRESS, animationProgress, 0); a = va; - a.setInterpolator(DEACCEL_1_5); + a.setInterpolator(DECELERATE_1_5); a.setDuration(REORDER_ANIMATION_DURATION); a.start(); } @@ -2736,11 +2736,13 @@ public class CellLayout extends ViewGroup { } public boolean isOccupied(int x, int y) { - if (x < mCountX && y < mCountY) { + if (x >= 0 && x < mCountX && y >= 0 && y < mCountY) { return mOccupied.cells[x][y]; - } else { + } + if (BuildConfig.IS_STUDIO_BUILD) { throw new RuntimeException("Position exceeds the bound of this CellLayout"); } + return true; } @Override diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index fa1cbd60db..936229ab29 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -16,13 +16,13 @@ package com.android.launcher3; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.InvariantDeviceProfile.INDEX_DEFAULT; import static com.android.launcher3.InvariantDeviceProfile.INDEX_LANDSCAPE; import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_LANDSCAPE; import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_PORTRAIT; import static com.android.launcher3.Utilities.dpiFromPx; import static com.android.launcher3.Utilities.pxFromSp; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.config.FeatureFlags.ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH; import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR; import static com.android.launcher3.icons.GraphicsUtils.getShapePath; @@ -53,13 +53,20 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.DotRenderer; import com.android.launcher3.icons.IconNormalizer; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.responsive.AllAppsSpecs; +import com.android.launcher3.responsive.CalculatedAllAppsSpec; +import com.android.launcher3.responsive.CalculatedFolderSpec; +import com.android.launcher3.responsive.CalculatedHotseatSpec; +import com.android.launcher3.responsive.CalculatedWorkspaceSpec; +import com.android.launcher3.responsive.FolderSpecs; +import com.android.launcher3.responsive.HotseatSpecs; +import com.android.launcher3.responsive.WorkspaceSpecs; import com.android.launcher3.uioverrides.ApiWrapper; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.Info; +import com.android.launcher3.util.IconSizeSteps; import com.android.launcher3.util.ResourceHelper; import com.android.launcher3.util.WindowBounds; -import com.android.launcher3.workspace.CalculatedWorkspaceSpec; -import com.android.launcher3.workspace.WorkspaceSpecs; import java.io.PrintWriter; import java.util.Locale; @@ -80,6 +87,7 @@ public class DeviceProfile { public final InvariantDeviceProfile inv; private final Info mInfo; private final DisplayMetrics mMetrics; + private final IconSizeSteps mIconSizeSteps; // Device properties public final boolean isTablet; @@ -104,14 +112,18 @@ public class DeviceProfile { public final float aspectRatio; - public final boolean isScalableGrid; + private final boolean mIsScalableGrid; private final int mTypeIndex; // Responsive grid private final boolean mIsResponsiveGrid; - private WorkspaceSpecs mWorkspaceSpecs; private CalculatedWorkspaceSpec mResponsiveWidthSpec; private CalculatedWorkspaceSpec mResponsiveHeightSpec; + private CalculatedAllAppsSpec mAllAppsResponsiveWidthSpec; + private CalculatedAllAppsSpec mAllAppsResponsiveHeightSpec; + private CalculatedFolderSpec mResponsiveFolderWidthSpec; + private CalculatedFolderSpec mResponsiveFolderHeightSpec; + private CalculatedHotseatSpec mResponsiveHotseatSpec; /** * The maximum amount of left/right workspace padding as a percentage of the screen width. @@ -152,14 +164,15 @@ public class DeviceProfile { public int iconSizePx; public int iconTextSizePx; public int iconDrawablePaddingPx; - public int iconDrawablePaddingOriginalPx; + private final int mIconDrawablePaddingOriginalPx; + public boolean iconCenterVertically; public float cellScaleToFit; public int cellWidthPx; public int cellHeightPx; public int workspaceCellPaddingXPx; - public int cellYPaddingPx; + public int cellYPaddingPx = -1; // Folder public float folderLabelTextScale; @@ -169,7 +182,7 @@ public class DeviceProfile { public int folderIconOffsetYPx; // Folder content - public int folderCellLayoutBorderSpacePx; + public Point folderCellLayoutBorderSpacePx; public int folderContentPaddingLeftRight; public int folderContentPaddingTop; @@ -281,6 +294,7 @@ public class DeviceProfile { public final int taskbarIconSize; // If true, used to layout taskbar in 3 button navigation mode. public final boolean startAlignTaskbar; + public final boolean isTransientTaskbar; // DragController public int flingToDeleteThresholdVelocity; @@ -304,9 +318,12 @@ public class DeviceProfile { mInsets.set(windowBounds.insets); // TODO(b/241386436): shouldn't change any launcher behaviour - mIsResponsiveGrid = inv.workspaceSpecsId != INVALID_RESOURCE_HANDLE; + mIsResponsiveGrid = inv.workspaceSpecsId != INVALID_RESOURCE_HANDLE + && inv.allAppsSpecsId != INVALID_RESOURCE_HANDLE + && inv.folderSpecsId != INVALID_RESOURCE_HANDLE + && inv.hotseatSpecsId != INVALID_RESOURCE_HANDLE; - isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode; + mIsScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode; // Determine device posture. mInfo = info; isTablet = info.isTablet(windowBounds); @@ -322,6 +339,8 @@ public class DeviceProfile { final Resources res = context.getResources(); mMetrics = res.getDisplayMetrics(); + mIconSizeSteps = mIsResponsiveGrid ? new IconSizeSteps(res) : null; + // Determine sizes. widthPx = windowBounds.bounds.width(); heightPx = windowBounds.bounds.height(); @@ -343,15 +362,11 @@ public class DeviceProfile { } } - if (mIsResponsiveGrid) { - mWorkspaceSpecs = new WorkspaceSpecs(new ResourceHelper(context, inv.workspaceSpecsId)); - mResponsiveWidthSpec = mWorkspaceSpecs.getCalculatedWidthSpec(inv.numColumns, - availableWidthPx); - mResponsiveHeightSpec = mWorkspaceSpecs.getCalculatedHeightSpec(inv.numRows, - availableHeightPx); - } - - if (DisplayController.isTransientTaskbar(context)) { + isTransientTaskbar = DisplayController.isTransientTaskbar(context); + if (!isTaskbarPresent) { + taskbarIconSize = taskbarHeight = stashedTaskbarHeight = taskbarBottomMargin = 0; + startAlignTaskbar = false; + } else if (isTransientTaskbar) { float invTransientIconSizeDp = inv.transientTaskbarIconSize[mTypeIndex]; taskbarIconSize = pxFromDp(invTransientIconSizeDp, mMetrics); taskbarHeight = Math.round((taskbarIconSize * ICON_VISIBLE_AREA_FACTOR) @@ -373,8 +388,6 @@ public class DeviceProfile { edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); workspaceContentScale = res.getFloat(R.dimen.workspace_content_scale); - desiredWorkspaceHorizontalMarginPx = getHorizontalMarginPx(inv, res); - desiredWorkspaceHorizontalMarginOriginalPx = desiredWorkspaceHorizontalMarginPx; gridVisualizationPaddingX = res.getDimensionPixelSize( R.dimen.grid_visualization_horizontal_cell_spacing); gridVisualizationPaddingY = res.getDimensionPixelSize( @@ -407,7 +420,7 @@ public class DeviceProfile { folderLabelTextScale = res.getFloat(R.dimen.folder_label_text_scale); - if (isScalableGrid && inv.folderStyle != INVALID_RESOURCE_HANDLE) { + if (mIsScalableGrid && inv.folderStyle != INVALID_RESOURCE_HANDLE) { TypedArray folderStyle = context.obtainStyledAttributes(inv.folderStyle, R.styleable.FolderStyle); // These are re-set in #updateFolderCellSize if the grid is not scalable @@ -418,19 +431,19 @@ public class DeviceProfile { folderContentPaddingTop = folderStyle.getDimensionPixelSize( R.styleable.FolderStyle_folderTopPadding, 0); - folderCellLayoutBorderSpacePx = folderStyle.getDimensionPixelSize( + + int gutter = folderStyle.getDimensionPixelSize( R.styleable.FolderStyle_folderBorderSpace, 0); + folderCellLayoutBorderSpacePx = new Point(gutter, gutter); folderFooterHeightPx = folderStyle.getDimensionPixelSize( R.styleable.FolderStyle_folderFooterHeight, 0); folderStyle.recycle(); - } else { - folderCellLayoutBorderSpacePx = 0; + } else if (!mIsResponsiveGrid) { + folderCellLayoutBorderSpacePx = new Point(0, 0); folderFooterHeightPx = res.getDimensionPixelSize(R.dimen.folder_footer_height_default); folderContentPaddingTop = res.getDimensionPixelSize(R.dimen.folder_top_padding_default); } - cellLayoutBorderSpacePx = getCellLayoutBorderSpace(inv); - cellLayoutBorderSpaceOriginalPx = new Point(cellLayoutBorderSpacePx); allAppsBorderSpacePx = new Point( pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].x, mMetrics), pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].y, mMetrics)); @@ -449,7 +462,7 @@ public class DeviceProfile { cellStyle = context.obtainStyledAttributes(R.style.CellStyleDefault, R.styleable.CellStyle); } - iconDrawablePaddingOriginalPx = cellStyle.getDimensionPixelSize( + mIconDrawablePaddingOriginalPx = cellStyle.getDimensionPixelSize( R.styleable.CellStyle_iconDrawablePadding, 0); cellStyle.recycle(); @@ -480,7 +493,7 @@ public class DeviceProfile { || inv.inlineQsb[INDEX_TWO_PANEL_LANDSCAPE] : inv.inlineQsb[INDEX_DEFAULT] || inv.inlineQsb[INDEX_LANDSCAPE]) && hotseatQsbHeight > 0; - isQsbInline = isScalableGrid && inv.inlineQsb[mTypeIndex] && canQsbInline; + isQsbInline = mIsScalableGrid && inv.inlineQsb[mTypeIndex] && canQsbInline; areNavButtonsInline = isTaskbarPresent && !isGestureMode; numShownHotseatIcons = @@ -491,7 +504,17 @@ public class DeviceProfile { int hotseatBarBottomSpace = pxFromDp(inv.hotseatBarBottomSpace[mTypeIndex], mMetrics); int minQsbMargin = res.getDimensionPixelSize(R.dimen.min_qsb_margin); - hotseatQsbSpace = pxFromDp(inv.hotseatQsbSpace[mTypeIndex], mMetrics); + + if (mIsResponsiveGrid) { + HotseatSpecs hotseatSpecs = + HotseatSpecs.create(new ResourceHelper(context, + isTwoPanels ? inv.hotseatSpecsTwoPanelId : inv.hotseatSpecsId)); + mResponsiveHotseatSpec = hotseatSpecs.getCalculatedHeightSpec(heightPx); + hotseatQsbSpace = mResponsiveHotseatSpec.getHotseatQsbSpace(); + } else { + hotseatQsbSpace = pxFromDp(inv.hotseatQsbSpace[mTypeIndex], mMetrics); + } + // Have a little space between the inset and the QSB if (mInsets.bottom + minQsbMargin > hotseatBarBottomSpace) { int availableSpace = hotseatQsbSpace - (mInsets.bottom - hotseatBarBottomSpace); @@ -535,6 +558,44 @@ public class DeviceProfile { hotseatBarEndOffset = 0; } + // Needs to be calculated after hotseatBarSizePx is correct, + // for the available height to be correct + if (mIsResponsiveGrid) { + WorkspaceSpecs workspaceSpecs = WorkspaceSpecs.create( + new ResourceHelper(context, + isTwoPanels ? inv.workspaceSpecsTwoPanelId : inv.workspaceSpecsId)); + int availableResponsiveWidth = + availableWidthPx - (isVerticalBarLayout() ? hotseatBarSizePx : 0); + int numColumns = getPanelCount() * inv.numColumns; + // don't use availableHeightPx because it subtracts bottom padding, + // but the workspace go behind it + int availableResponsiveHeight = + heightPx - mInsets.top - (isVerticalBarLayout() ? 0 : hotseatBarSizePx); + mResponsiveWidthSpec = workspaceSpecs.getCalculatedWidthSpec(numColumns, + availableResponsiveWidth); + mResponsiveHeightSpec = workspaceSpecs.getCalculatedHeightSpec(inv.numRows, + availableResponsiveHeight); + + AllAppsSpecs allAppsSpecs = AllAppsSpecs.create( + new ResourceHelper(context, + isTwoPanels ? inv.allAppsSpecsTwoPanelId : inv.allAppsSpecsId)); + mAllAppsResponsiveWidthSpec = allAppsSpecs.getCalculatedWidthSpec(numColumns, + mResponsiveWidthSpec.getAvailableSpace(), mResponsiveWidthSpec); + mAllAppsResponsiveHeightSpec = allAppsSpecs.getCalculatedHeightSpec(inv.numRows, + mResponsiveHeightSpec.getAvailableSpace(), mResponsiveHeightSpec); + + FolderSpecs folderSpecs = FolderSpecs.create( + new ResourceHelper(context, + isTwoPanels ? inv.folderSpecsTwoPanelId : inv.folderSpecsId)); + mResponsiveFolderWidthSpec = folderSpecs.getCalculatedWidthSpec(inv.numFolderColumns, + mResponsiveWidthSpec.getAvailableSpace(), mResponsiveWidthSpec); + mResponsiveFolderHeightSpec = folderSpecs.getCalculatedHeightSpec(inv.numFolderRows, + mResponsiveHeightSpec.getAvailableSpace(), mResponsiveHeightSpec); + } + + desiredWorkspaceHorizontalMarginPx = getHorizontalMarginPx(inv, res); + desiredWorkspaceHorizontalMarginOriginalPx = desiredWorkspaceHorizontalMarginPx; + overviewTaskMarginPx = res.getDimensionPixelSize(R.dimen.overview_task_margin); overviewTaskIconSizePx = res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_size); overviewTaskIconDrawableSizePx = @@ -542,7 +603,9 @@ public class DeviceProfile { overviewTaskIconDrawableSizeGridPx = res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_drawable_size_grid); overviewTaskThumbnailTopMarginPx = overviewTaskIconSizePx + overviewTaskMarginPx; - overviewActionsTopMarginPx = res.getDimensionPixelSize(R.dimen.overview_actions_top_margin); + // Don't add margin with floating search bar to minimize risk of overlapping. + overviewActionsTopMarginPx = FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() ? 0 + : res.getDimensionPixelSize(R.dimen.overview_actions_top_margin); overviewPageSpacing = res.getDimensionPixelSize(R.dimen.overview_page_spacing); overviewActionsButtonSpacing = res.getDimensionPixelSize( R.dimen.overview_actions_button_spacing); @@ -555,21 +618,7 @@ public class DeviceProfile { // Calculate all of the remaining variables. extraSpace = updateAvailableDimensions(res); - // Now that we have all of the variables calculated, we can tune certain sizes. - if (isScalableGrid && inv.devicePaddingId != INVALID_RESOURCE_HANDLE) { - // Paddings were created assuming no scaling, so we first unscale the extra space. - int unscaledExtraSpace = (int) (extraSpace / cellScaleToFit); - DevicePaddings devicePaddings = new DevicePaddings(context, inv.devicePaddingId); - DevicePadding padding = devicePaddings.getDevicePadding(unscaledExtraSpace); - maxEmptySpace = padding.getMaxEmptySpacePx(); - - int paddingWorkspaceTop = padding.getWorkspaceTopPadding(unscaledExtraSpace); - int paddingWorkspaceBottom = padding.getWorkspaceBottomPadding(unscaledExtraSpace); - int paddingHotseatBottom = padding.getHotseatBottomPadding(unscaledExtraSpace); - - workspaceTopPadding = Math.round(paddingWorkspaceTop * cellScaleToFit); - workspaceBottomPadding = Math.round(paddingWorkspaceBottom * cellScaleToFit); - } + calculateAndSetWorkspaceVerticalPadding(context, inv, extraSpace); int cellLayoutPadding = isTwoPanels ? cellLayoutBorderSpacePx.x / 2 : res.getDimensionPixelSize( @@ -588,6 +637,10 @@ public class DeviceProfile { // Hotseat and QSB width depends on updated cellSize and workspace padding recalculateHotseatWidthAndBorderSpace(); + if (mIsResponsiveGrid && isVerticalBarLayout()) { + hotseatBorderSpace = cellLayoutBorderSpacePx.y; + } + // AllApps height calculation depends on updated cellSize if (isTablet) { int collapseHandleHeight = @@ -650,19 +703,44 @@ public class DeviceProfile { } private int getHorizontalMarginPx(InvariantDeviceProfile idp, Resources res) { + if (mIsResponsiveGrid) { + return mResponsiveWidthSpec.getStartPaddingPx(); + } + if (isVerticalBarLayout()) { return 0; } - return isScalableGrid + return mIsScalableGrid ? pxFromDp(idp.horizontalMargin[mTypeIndex], mMetrics) : res.getDimensionPixelSize(R.dimen.dynamic_grid_left_right_margin); } + private void calculateAndSetWorkspaceVerticalPadding(Context context, + InvariantDeviceProfile inv, + int extraSpace) { + if (mIsResponsiveGrid) { + workspaceTopPadding = mResponsiveHeightSpec.getStartPaddingPx(); + workspaceBottomPadding = mResponsiveHeightSpec.getEndPaddingPx(); + } else if (mIsScalableGrid && inv.devicePaddingId != INVALID_RESOURCE_HANDLE) { + // Paddings were created assuming no scaling, so we first unscale the extra space. + int unscaledExtraSpace = (int) (extraSpace / cellScaleToFit); + DevicePaddings devicePaddings = new DevicePaddings(context, inv.devicePaddingId); + DevicePadding padding = devicePaddings.getDevicePadding(unscaledExtraSpace); + maxEmptySpace = padding.getMaxEmptySpacePx(); + + int paddingWorkspaceTop = padding.getWorkspaceTopPadding(unscaledExtraSpace); + int paddingWorkspaceBottom = padding.getWorkspaceBottomPadding(unscaledExtraSpace); + + workspaceTopPadding = Math.round(paddingWorkspaceTop * cellScaleToFit); + workspaceBottomPadding = Math.round(paddingWorkspaceBottom * cellScaleToFit); + } + } + /** Updates hotseatCellHeightPx and hotseatBarSizePx */ private void updateHotseatSizes(int hotseatIconSizePx) { // Ensure there is enough space for folder icons, which have a slightly larger radius. - hotseatCellHeightPx = (int) Math.ceil(hotseatIconSizePx * ICON_OVERLAP_FACTOR); + hotseatCellHeightPx = getIconSizeWithOverlap(hotseatIconSizePx); if (isVerticalBarLayout()) { hotseatBarSizePx = hotseatIconSizePx + hotseatBarSidePaddingStartPx @@ -683,7 +761,7 @@ public class DeviceProfile { * necessary. */ public void recalculateHotseatWidthAndBorderSpace() { - if (!isScalableGrid) return; + if (!mIsScalableGrid) return; int columns = inv.hotseatColumnSpan[mTypeIndex]; float hotseatWidthPx = getIconToIconWidthForColumns(columns); @@ -728,7 +806,6 @@ public class DeviceProfile { hotseatBorderSpace = calculateHotseatBorderSpace(maxHotseatIconsWidthPx, (isQsbInline ? 1 : 0) + /* border between nav buttons and first icon */ 1); } while (hotseatBorderSpace < mMinHotseatIconSpacePx && numShownHotseatIcons > 1); - } private Point getCellLayoutBorderSpace(InvariantDeviceProfile idp) { @@ -736,12 +813,16 @@ public class DeviceProfile { } private Point getCellLayoutBorderSpace(InvariantDeviceProfile idp, float scale) { - if (!isScalableGrid) { - return new Point(0, 0); - } + int horizontalSpacePx = 0; + int verticalSpacePx = 0; - int horizontalSpacePx = pxFromDp(idp.borderSpaces[mTypeIndex].x, mMetrics, scale); - int verticalSpacePx = pxFromDp(idp.borderSpaces[mTypeIndex].y, mMetrics, scale); + if (mIsResponsiveGrid) { + horizontalSpacePx = mResponsiveWidthSpec.getGutterPx(); + verticalSpacePx = mResponsiveHeightSpec.getGutterPx(); + } else if (mIsScalableGrid) { + horizontalSpacePx = pxFromDp(idp.borderSpaces[mTypeIndex].x, mMetrics, scale); + verticalSpacePx = pxFromDp(idp.borderSpaces[mTypeIndex].y, mMetrics, scale); + } return new Point(horizontalSpacePx, verticalSpacePx); } @@ -807,54 +888,29 @@ public class DeviceProfile { float workspaceCellPaddingY = getCellSize().y - iconSizePx - iconDrawablePaddingPx - iconTextHeight; + if (mIsResponsiveGrid) { + // Hide text only if doesn't fit inside the cell for responsive grid + if (workspaceCellPaddingY < 0) { + iconTextSizePx = 0; + iconDrawablePaddingPx = 0; + int iconSizeWithOverlap = getIconSizeWithOverlap(iconSizePx); + cellYPaddingPx = Math.max(0, getCellSize().y - iconSizeWithOverlap) / 2; + autoResizeAllAppsCells(); + } + + return; + } + // We want enough space so that the text is closer to its corresponding icon. if (workspaceCellPaddingY < iconTextHeight) { iconTextSizePx = 0; iconDrawablePaddingPx = 0; - cellHeightPx = (int) Math.ceil(iconSizePx * ICON_OVERLAP_FACTOR); + cellHeightPx = getIconSizeWithOverlap(iconSizePx); autoResizeAllAppsCells(); } } /** - * Re-computes the all-apps cell size to be independent of workspace - */ - public void autoResizeAllAppsCells() { - int textHeight = Utilities.calculateTextHeight(allAppsIconTextSizePx); - int topBottomPadding = textHeight; - allAppsCellHeightPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx - + textHeight + (topBottomPadding * 2); - } - - private void updateAllAppsContainerWidth(Resources res) { - int cellLayoutHorizontalPadding = - (cellLayoutPaddingPx.left + cellLayoutPaddingPx.right) / 2; - if (isTablet) { - int usedWidth = (allAppsCellWidthPx * numShownAllAppsColumns) - + (allAppsBorderSpacePx.x * (numShownAllAppsColumns - 1)) - + allAppsLeftRightPadding * 2; - allAppsLeftRightMargin = Math.max(1, (availableWidthPx - usedWidth) / 2); - } else { - allAppsLeftRightPadding = - desiredWorkspaceHorizontalMarginPx + cellLayoutHorizontalPadding; - } - } - - private void setupAllAppsStyle(Context context) { - TypedArray allAppsStyle; - if (inv.allAppsStyle != INVALID_RESOURCE_HANDLE) { - allAppsStyle = context.obtainStyledAttributes(inv.allAppsStyle, - R.styleable.AllAppsStyle); - } else { - allAppsStyle = context.obtainStyledAttributes(R.style.AllAppsStyleDefault, - R.styleable.AllAppsStyle); - } - allAppsLeftRightPadding = allAppsStyle.getDimensionPixelSize( - R.styleable.AllAppsStyle_horizontalPadding, 0); - allAppsStyle.recycle(); - } - - /** * Returns the amount of extra (or unused) vertical space. */ private int updateAvailableDimensions(Resources res) { @@ -862,11 +918,15 @@ public class DeviceProfile { float invIconTextSizeSp = inv.iconTextSize[mTypeIndex]; iconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics)); iconTextSizePx = pxFromSp(invIconTextSizeSp, mMetrics); + iconCenterVertically = mIsScalableGrid || mIsResponsiveGrid; updateIconSize(1f, res); - updateWorkspacePadding(); + if (mIsResponsiveGrid) { + return 0; + } + // Check to see if the icons fit within the available height. float usedHeight = getCellLayoutHeightSpecification(); final int maxHeight = getCellLayoutHeight(); @@ -875,7 +935,7 @@ public class DeviceProfile { boolean shouldScale = scaleY < 1f; float scaleX = 1f; - if (isScalableGrid) { + if (mIsScalableGrid) { // We scale to fit the cellWidth and cellHeight in the available space. // The benefit of scalable grids is that we can get consistent aspect ratios between // devices. @@ -906,6 +966,29 @@ public class DeviceProfile { + cellLayoutPaddingPx.left + cellLayoutPaddingPx.right; } + private int getNormalizedIconDrawablePadding() { + // TODO(b/235886078): workaround needed because of this bug + // Icons are 10% larger on XML than their visual size, + // so remove that extra space to get labels closer to the correct padding + int iconVisibleSizePx = (int) Math.round(ICON_VISIBLE_AREA_FACTOR * iconSizePx); + return Math.max(0, mIconDrawablePaddingOriginalPx - ((iconSizePx - iconVisibleSizePx) / 2)); + } + + private int getNormalizedFolderChildDrawablePaddingPx(int textHeight) { + // TODO(b/235886078): workaround needed because of this bug + // Icons are 10% larger on XML than their visual size, + // so remove that extra space to get labels closer to the correct padding + int drawablePadding = (folderCellHeightPx - folderChildIconSizePx - textHeight) / 3; + + int iconVisibleSizePx = Math.round(ICON_VISIBLE_AREA_FACTOR * folderChildIconSizePx); + int iconSizeDiff = folderChildIconSizePx - iconVisibleSizePx; + return Math.max(0, drawablePadding - iconSizeDiff / 2); + } + + private int getIconSizeWithOverlap(int iconSize) { + return (int) Math.ceil(iconSize * ICON_OVERLAP_FACTOR); + } + /** * Updating the iconSize affects many aspects of the launcher layout, such as: iconSizePx, * iconTextSizePx, iconDrawablePaddingPx, cellWidth/Height, allApps* variants, @@ -918,10 +1001,36 @@ public class DeviceProfile { // Workspace final boolean isVerticalLayout = isVerticalBarLayout(); - iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * iconScale); cellLayoutBorderSpacePx = getCellLayoutBorderSpace(inv, scale); - if (isScalableGrid) { + if (mIsResponsiveGrid) { + cellWidthPx = mResponsiveWidthSpec.getCellSizePx(); + cellHeightPx = mResponsiveHeightSpec.getCellSizePx(); + + if (cellWidthPx < iconSizePx) { + // get a smaller icon size + iconSizePx = mIconSizeSteps.getIconSmallerThan(cellWidthPx); + } + + iconDrawablePaddingPx = getNormalizedIconDrawablePadding(); + int iconTextHeight = Utilities.calculateTextHeight(iconTextSizePx); + int cellContentHeight = iconSizePx + iconDrawablePaddingPx + iconTextHeight; + + while (iconSizePx > mIconSizeSteps.minimumIconSize() + && cellContentHeight > cellHeightPx) { + iconDrawablePaddingPx -= cellContentHeight - cellHeightPx; + if (iconDrawablePaddingPx < 0) { + // get a smaller icon size + iconSizePx = mIconSizeSteps.getNextLowerIconSize(iconSizePx); + iconDrawablePaddingPx = getNormalizedIconDrawablePadding(); + } + // calculate new cellContentHeight + cellContentHeight = iconSizePx + iconDrawablePaddingPx + iconTextHeight; + } + + cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2; + } else if (mIsScalableGrid) { + iconDrawablePaddingPx = (int) (getNormalizedIconDrawablePadding() * iconScale); cellWidthPx = pxFromDp(inv.minCellSize[mTypeIndex].x, mMetrics, scale); cellHeightPx = pxFromDp(inv.minCellSize[mTypeIndex].y, mMetrics, scale); @@ -980,8 +1089,9 @@ public class DeviceProfile { desiredWorkspaceHorizontalMarginPx = (int) (desiredWorkspaceHorizontalMarginOriginalPx * scale); } else { + iconDrawablePaddingPx = (int) (getNormalizedIconDrawablePadding() * iconScale); cellWidthPx = iconSizePx + iconDrawablePaddingPx; - cellHeightPx = (int) Math.ceil(iconSizePx * ICON_OVERLAP_FACTOR) + cellHeightPx = getIconSizeWithOverlap(iconSizePx) + iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx); int cellPaddingY = (getCellSize().y - cellHeightPx) / 2; @@ -996,7 +1106,15 @@ public class DeviceProfile { } // All apps - updateAllAppsIconSize(scale, res); + if (mIsResponsiveGrid) { + updateAllAppsWithResponsiveMeasures(); + } else { + updateAllAppsIconSize(scale, res); + } + updateAllAppsContainerWidth(); + if (isVerticalBarLayout()) { + hideWorkspaceLabelsIfNotEnoughSpace(); + } updateHotseatSizes(iconSizePx); @@ -1021,14 +1139,14 @@ public class DeviceProfile { * This method calculates the space between the icons to achieve a certain width. */ private int calculateHotseatBorderSpace(float hotseatWidthPx, int numExtraBorder) { + int numBorders = (numShownHotseatIcons - 1 + numExtraBorder); + if (numBorders <= 0) return 0; + float hotseatIconsTotalPx = iconSizePx * numShownHotseatIcons; - int hotseatBorderSpacePx = - (int) (hotseatWidthPx - hotseatIconsTotalPx) - / (numShownHotseatIcons - 1 + numExtraBorder); + int hotseatBorderSpacePx = (int) (hotseatWidthPx - hotseatIconsTotalPx) / numBorders; return Math.min(hotseatBorderSpacePx, mMaxHotseatIconSpacePx); } - /** * Updates the iconSize for allApps* variants. */ @@ -1042,10 +1160,10 @@ public class DeviceProfile { + allAppsBorderSpacePx.y; // but width is just the cell, // the border is added in #updateAllAppsContainerWidth - if (isScalableGrid) { + if (mIsScalableGrid) { allAppsIconSizePx = pxFromDp(inv.allAppsIconSize[mTypeIndex], mMetrics); allAppsIconTextSizePx = pxFromSp(inv.allAppsIconTextSize[mTypeIndex], mMetrics); - allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx; + allAppsIconDrawablePaddingPx = getNormalizedIconDrawablePadding(); allAppsCellWidthPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].x, mMetrics, scale); if (allAppsCellWidthPx < allAppsIconSizePx) { @@ -1082,22 +1200,70 @@ public class DeviceProfile { res.getDimensionPixelSize(R.dimen.all_apps_icon_drawable_padding); allAppsCellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx); } + } - updateAllAppsContainerWidth(res); - if (isVerticalBarLayout()) { - hideWorkspaceLabelsIfNotEnoughSpace(); + private void updateAllAppsWithResponsiveMeasures() { + allAppsIconSizePx = iconSizePx; + allAppsIconTextSizePx = iconTextSizePx; + allAppsIconDrawablePaddingPx = iconDrawablePaddingPx; + + allAppsBorderSpacePx = new Point( + mAllAppsResponsiveWidthSpec.getGutterPx(), + mAllAppsResponsiveHeightSpec.getGutterPx() + ); + allAppsCellHeightPx = mAllAppsResponsiveHeightSpec.getCellSizePx() + + mAllAppsResponsiveHeightSpec.getGutterPx(); + allAppsCellWidthPx = mAllAppsResponsiveWidthSpec.getCellSizePx(); + allAppsLeftRightPadding = mAllAppsResponsiveWidthSpec.getStartPaddingPx(); + } + + /** + * Re-computes the all-apps cell size to be independent of workspace + */ + public void autoResizeAllAppsCells() { + int textHeight = Utilities.calculateTextHeight(allAppsIconTextSizePx); + int topBottomPadding = textHeight; + allAppsCellHeightPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx + + textHeight + (topBottomPadding * 2); + } + + private void updateAllAppsContainerWidth() { + int cellLayoutHorizontalPadding = + (cellLayoutPaddingPx.left + cellLayoutPaddingPx.right) / 2; + if (isTablet) { + int usedWidth = (allAppsCellWidthPx * numShownAllAppsColumns) + + (allAppsBorderSpacePx.x * (numShownAllAppsColumns - 1)) + + allAppsLeftRightPadding * 2; + allAppsLeftRightMargin = Math.max(1, (availableWidthPx - usedWidth) / 2); + } else { + allAppsLeftRightPadding = + Math.max(0, desiredWorkspaceHorizontalMarginPx + cellLayoutHorizontalPadding + - (allAppsBorderSpacePx.x / 2)); } } + private void setupAllAppsStyle(Context context) { + TypedArray allAppsStyle = context.obtainStyledAttributes( + inv.allAppsStyle != INVALID_RESOURCE_HANDLE ? inv.allAppsStyle + : R.style.AllAppsStyleDefault, R.styleable.AllAppsStyle); + + allAppsLeftRightPadding = allAppsStyle.getDimensionPixelSize( + R.styleable.AllAppsStyle_horizontalPadding, 0); + allAppsStyle.recycle(); + } + private void updateAvailableFolderCellDimensions(Resources res) { updateFolderCellSize(1f, res); + // Responsive grid doesn't need to scale the folder + if (mIsResponsiveGrid) return; + // For usability we can't have the folder use the whole width of the screen Point totalWorkspacePadding = getTotalWorkspacePadding(); // Check if the folder fit within the available height. float contentUsedHeight = folderCellHeightPx * inv.numFolderRows - + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacePx) + + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacePx.y) + folderFooterHeightPx + folderContentPaddingTop; int contentMaxHeight = availableHeightPx - totalWorkspacePadding.y; @@ -1105,7 +1271,7 @@ public class DeviceProfile { // Check if the folder fit within the available width. float contentUsedWidth = folderCellWidthPx * inv.numFolderColumns - + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacePx) + + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacePx.x) + folderContentPaddingLeftRight * 2; int contentMaxWidth = availableWidthPx - totalWorkspacePadding.x; float scaleX = contentMaxWidth / contentUsedWidth; @@ -1125,7 +1291,45 @@ public class DeviceProfile { int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx); - if (isScalableGrid) { + if (mIsResponsiveGrid) { + folderCellWidthPx = mResponsiveFolderWidthSpec.getCellSizePx(); + + // Height + folderCellHeightPx = mResponsiveFolderHeightSpec.getCellSizePx(); + folderContentPaddingTop = mResponsiveFolderHeightSpec.getStartPaddingPx(); + folderFooterHeightPx = mResponsiveFolderHeightSpec.getEndPaddingPx(); + + folderCellLayoutBorderSpacePx = new Point(mResponsiveFolderWidthSpec.getGutterPx(), + mResponsiveFolderHeightSpec.getGutterPx()); + + folderContentPaddingLeftRight = mResponsiveFolderWidthSpec.getStartPaddingPx(); + + // Reduce icon width if it's wider than the expected folder cell width + if (folderCellWidthPx < folderChildIconSizePx) { + folderChildIconSizePx = mIconSizeSteps.getIconSmallerThan(folderCellWidthPx); + } + + // Recalculating padding and cell height + folderChildDrawablePaddingPx = getNormalizedFolderChildDrawablePaddingPx(textHeight); + int folderCellContentHeight = folderChildIconSizePx + folderChildDrawablePaddingPx + + textHeight; + + // Reduce the icon in height when it's taller than the expected cell height + while (folderChildIconSizePx > mIconSizeSteps.minimumIconSize() + && folderCellContentHeight > folderCellHeightPx) { + folderChildDrawablePaddingPx -= folderCellContentHeight - folderCellHeightPx; + if (folderChildDrawablePaddingPx < 0) { + // get a smaller icon size + folderChildIconSizePx = mIconSizeSteps.getNextLowerIconSize( + folderChildIconSizePx); + folderChildDrawablePaddingPx = + getNormalizedFolderChildDrawablePaddingPx(textHeight); + } + // calculate new cellContentHeight + folderCellContentHeight = folderChildIconSizePx + folderChildDrawablePaddingPx + + textHeight; + } + } else if (mIsScalableGrid) { if (inv.folderStyle == INVALID_RESOURCE_HANDLE) { folderCellWidthPx = roundPxValueFromFloat(getCellSize().x * scale); folderCellHeightPx = roundPxValueFromFloat(getCellSize().y * scale); @@ -1135,11 +1339,15 @@ public class DeviceProfile { } folderContentPaddingTop = roundPxValueFromFloat(folderContentPaddingTop * scale); - folderCellLayoutBorderSpacePx = roundPxValueFromFloat( - folderCellLayoutBorderSpacePx * scale); + folderCellLayoutBorderSpacePx = new Point( + roundPxValueFromFloat(folderCellLayoutBorderSpacePx.x * scale), + roundPxValueFromFloat(folderCellLayoutBorderSpacePx.y * scale) + ); folderFooterHeightPx = roundPxValueFromFloat(folderFooterHeightPx * scale); - folderContentPaddingLeftRight = folderCellLayoutBorderSpacePx; + folderContentPaddingLeftRight = folderCellLayoutBorderSpacePx.x; + + folderChildDrawablePaddingPx = getNormalizedFolderChildDrawablePaddingPx(textHeight); } else { int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding) * scale); @@ -1156,10 +1364,8 @@ public class DeviceProfile { res.getDimensionPixelSize(R.dimen.folder_footer_height_default) * scale); + folderChildDrawablePaddingPx = getNormalizedFolderChildDrawablePaddingPx(textHeight); } - - folderChildDrawablePaddingPx = Math.max(0, - (folderCellHeightPx - folderChildIconSizePx - textHeight) / 3); } public void updateInsets(Rect insets) { @@ -1288,22 +1494,36 @@ public class DeviceProfile { private void updateWorkspacePadding() { Rect padding = workspacePadding; if (isVerticalBarLayout()) { - padding.top = 0; - padding.bottom = edgeMarginPx; - if (isSeascape()) { - padding.left = hotseatBarSizePx; - padding.right = hotseatBarSidePaddingStartPx; + if (mIsResponsiveGrid) { + padding.top = mResponsiveHeightSpec.getStartPaddingPx(); + padding.bottom = mResponsiveHeightSpec.getEndPaddingPx(); + if (isSeascape()) { + padding.left = hotseatBarSizePx + mResponsiveWidthSpec.getEndPaddingPx(); + padding.right = mResponsiveWidthSpec.getStartPaddingPx(); + } else { + padding.left = mResponsiveWidthSpec.getStartPaddingPx(); + padding.right = hotseatBarSizePx + mResponsiveWidthSpec.getEndPaddingPx(); + } } else { - padding.left = hotseatBarSidePaddingStartPx; - padding.right = hotseatBarSizePx; + padding.top = 0; + padding.bottom = edgeMarginPx; + if (isSeascape()) { + padding.left = hotseatBarSizePx; + padding.right = hotseatBarSidePaddingStartPx; + } else { + padding.left = hotseatBarSidePaddingStartPx; + padding.right = hotseatBarSizePx; + } } } else { // Pad the bottom of the workspace with hotseat bar // and leave a bit of space in case a widget go all the way down - int paddingBottom = hotseatBarSizePx + workspaceBottomPadding - + workspacePageIndicatorHeight - mWorkspacePageIndicatorOverlapWorkspace - - mInsets.bottom; - int paddingTop = workspaceTopPadding + (isScalableGrid ? 0 : edgeMarginPx); + int paddingBottom = hotseatBarSizePx + workspaceBottomPadding - mInsets.bottom; + if (!mIsResponsiveGrid) { + paddingBottom += + workspacePageIndicatorHeight - mWorkspacePageIndicatorOverlapWorkspace; + } + int paddingTop = workspaceTopPadding + (mIsScalableGrid ? 0 : edgeMarginPx); int paddingSide = desiredWorkspaceHorizontalMarginPx; padding.set(paddingSide, paddingTop, paddingSide, paddingBottom); @@ -1336,7 +1556,9 @@ public class DeviceProfile { // in vertical bar layout. // Workspace icons are moved up by a small factor. The variable diffOverlapFactor // is set to account for that difference. - float diffOverlapFactor = iconSizePx * (ICON_OVERLAP_FACTOR - 1) / 2; + float diffOverlapFactor = mIsResponsiveGrid ? 0 + : iconSizePx * (ICON_OVERLAP_FACTOR - 1) / 2; + int paddingTop = Math.max((int) (mInsets.top + cellLayoutPaddingPx.top - diffOverlapFactor), 0); int paddingBottom = Math.max((int) (mInsets.bottom + cellLayoutPaddingPx.bottom @@ -1379,7 +1601,7 @@ public class DeviceProfile { hotseatBarPadding.right = endSpacing; } - } else if (isScalableGrid) { + } else if (mIsScalableGrid) { int sideSpacing = (availableWidthPx - hotseatQsbWidth) / 2; hotseatBarPadding.set(sideSpacing, 0, @@ -1404,6 +1626,25 @@ public class DeviceProfile { return hotseatBarPadding; } + /** The margin between the edge of all apps and the edge of the first icon. */ + public int getAllAppsIconStartMargin() { + int allAppsSpacing; + if (isVerticalBarLayout()) { + // On phones, the landscape layout uses a different setup. + allAppsSpacing = workspacePadding.left + workspacePadding.right; + } else { + allAppsSpacing = allAppsLeftRightPadding * 2 + allAppsLeftRightMargin * 2; + } + + int cellWidth = DeviceProfile.calculateCellWidth( + availableWidthPx - allAppsSpacing, + 0 /* borderSpace */, + numShownAllAppsColumns); + int iconVisibleSize = Math.round(ICON_VISIBLE_AREA_FACTOR * allAppsIconSizePx); + int iconAlignmentMargin = (cellWidth - iconVisibleSize) / 2; + return allAppsLeftRightPadding + iconAlignmentMargin; + } + private int getAdditionalQsbSpace() { return isQsbInline ? hotseatQsbWidth + hotseatBorderSpace : 0; } @@ -1599,7 +1840,7 @@ public class DeviceProfile { writer.println(prefix + "\taspectRatio:" + aspectRatio); writer.println(prefix + "\tisResponsiveGrid:" + mIsResponsiveGrid); - writer.println(prefix + "\tisScalableGrid:" + isScalableGrid); + writer.println(prefix + "\tisScalableGrid:" + mIsScalableGrid); writer.println(prefix + "\tinv.numRows: " + inv.numRows); writer.println(prefix + "\tinv.numColumns: " + inv.numColumns); @@ -1639,8 +1880,10 @@ public class DeviceProfile { writer.println(prefix + pxToDpStr("folderChildTextSizePx", folderChildTextSizePx)); writer.println(prefix + pxToDpStr("folderChildDrawablePaddingPx", folderChildDrawablePaddingPx)); - writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx", - folderCellLayoutBorderSpacePx)); + writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx.x", + folderCellLayoutBorderSpacePx.x)); + writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx.y", + folderCellLayoutBorderSpacePx.y)); writer.println(prefix + pxToDpStr("folderContentPaddingLeftRight", folderContentPaddingLeftRight)); writer.println(prefix + pxToDpStr("folderTopPadding", folderContentPaddingTop)); @@ -1753,6 +1996,17 @@ public class DeviceProfile { getWorkspaceSpringLoadScale(context))); writer.println(prefix + pxToDpStr("getCellLayoutHeight()", getCellLayoutHeight())); writer.println(prefix + pxToDpStr("getCellLayoutWidth()", getCellLayoutWidth())); + if (mIsResponsiveGrid) { + writer.println(prefix + "\tmResponsiveHeightSpec:" + mResponsiveHeightSpec.toString()); + writer.println(prefix + "\tmResponsiveWidthSpec:" + mResponsiveWidthSpec.toString()); + writer.println(prefix + "\tmAllAppsResponsiveHeightSpec:" + + mAllAppsResponsiveHeightSpec.toString()); + writer.println(prefix + "\tmAllAppsResponsiveWidthSpec:" + + mAllAppsResponsiveWidthSpec.toString()); + writer.println(prefix + "\tmResponsiveFolderHeightSpec:" + mResponsiveFolderHeightSpec); + writer.println(prefix + "\tmResponsiveFolderWidthSpec:" + mResponsiveFolderWidthSpec); + writer.println(prefix + "\tmResponsiveHotseatSpec:" + mResponsiveHotseatSpec); + } } /** Returns a reduced representation of this DeviceProfile. */ diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java index addcac9f45..8bdf61a633 100644 --- a/src/com/android/launcher3/DropTargetBar.java +++ b/src/com/android/launcher3/DropTargetBar.java @@ -30,7 +30,7 @@ import android.view.ViewDebug; import android.view.ViewPropertyAnimator; import android.widget.FrameLayout; -import com.android.launcher3.anim.Interpolators; +import com.android.app.animation.Interpolators; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragController.DragListener; import com.android.launcher3.dragndrop.DragOptions; @@ -42,7 +42,7 @@ public class DropTargetBar extends FrameLayout implements DragListener, Insettable { protected static final int DEFAULT_DRAG_FADE_DURATION = 175; - protected static final TimeInterpolator DEFAULT_INTERPOLATOR = Interpolators.ACCEL; + protected static final TimeInterpolator DEFAULT_INTERPOLATOR = Interpolators.ACCELERATE; private final Runnable mFadeAnimationEndRunnable = () -> updateVisibility(DropTargetBar.this); diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java index f94a3c5521..8ec5c18393 100644 --- a/src/com/android/launcher3/ExtendedEditText.java +++ b/src/com/android/launcher3/ExtendedEditText.java @@ -21,6 +21,7 @@ import android.content.Context; import android.graphics.Rect; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Log; import android.view.DragEvent; import android.view.KeyEvent; import android.view.inputmethod.InputMethodManager; @@ -37,6 +38,8 @@ import java.util.Set; * Note: AppCompatEditText doesn't fully support #displayCompletions and #onCommitCompletion */ public class ExtendedEditText extends EditText { + private static final String TAG = "ExtendedEditText"; + private final Set<OnFocusChangeListener> mOnFocusChangeListeners = new HashSet<>(); private boolean mForceDisableSuggestions = false; @@ -71,14 +74,9 @@ public class ExtendedEditText extends EditText { @Override public boolean onKeyPreIme(int keyCode, KeyEvent event) { // If this is a back key, propagate the key back to the listener - if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { - if (TextUtils.isEmpty(getText())) { - hideKeyboard(); - } - if (mBackKeyListener != null) { - return mBackKeyListener.onBackKey(); - } - return false; + if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP + && mBackKeyListener != null) { + return mBackKeyListener.onBackKey(); } return super.onKeyPreIme(keyCode, event); } @@ -89,9 +87,17 @@ public class ExtendedEditText extends EditText { return false; } - public void showKeyboard() { + /** + * Synchronously shows the soft input method. + * + * @param shouldFocus whether this EditText should also request focus. + * @return true if the keyboard is shown correctly and focus is given to this view (if + * applicable). + */ + public boolean showKeyboard(boolean shouldFocus) { onKeyboardShown(); - showSoftInput(); + boolean focusResult = !shouldFocus || requestFocus(); + return focusResult && showSoftInputInternal(); } public void hideKeyboard() { @@ -104,10 +110,15 @@ public class ExtendedEditText extends EditText { .keyboardStateManager().setKeyboardState(SHOW); } - private boolean showSoftInput() { - return requestFocus() && - getContext().getSystemService(InputMethodManager.class) - .showSoftInput(this, InputMethodManager.SHOW_IMPLICIT); + private boolean showSoftInputInternal() { + boolean result = false; + InputMethodManager imm = getContext().getSystemService(InputMethodManager.class); + if (imm != null) { + result = imm.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT); + } else { + Log.w(TAG, "Failed to retrieve InputMethodManager from the system."); + } + return result; } public void dispatchBackKey() { diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 376f54dc4c..c619bc58de 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -18,7 +18,6 @@ package com.android.launcher3; import static com.android.launcher3.LauncherPrefs.GRID_NAME; import static com.android.launcher3.Utilities.dpiFromPx; -import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME; import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE; import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY; import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE; @@ -50,6 +49,7 @@ import androidx.core.content.res.ResourcesCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.DotRenderer; +import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.DeviceGridState; import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.testing.shared.ResourceUtils; @@ -180,6 +180,18 @@ public class InvariantDeviceProfile { public int devicePaddingId = INVALID_RESOURCE_HANDLE; @XmlRes public int workspaceSpecsId = INVALID_RESOURCE_HANDLE; + @XmlRes + public int workspaceSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; + @XmlRes + public int allAppsSpecsId = INVALID_RESOURCE_HANDLE; + @XmlRes + public int allAppsSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; + @XmlRes + public int folderSpecsId = INVALID_RESOURCE_HANDLE; + @XmlRes + public int folderSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; + public int hotseatSpecsId = INVALID_RESOURCE_HANDLE; + public int hotseatSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; public String dbFile; public int defaultLayoutId; @@ -275,12 +287,13 @@ public class InvariantDeviceProfile { * Reinitialize the current grid after a restore, where some grids might now be disabled. */ public void reinitializeAfterRestore(Context context) { + FileLog.d(TAG, "Reinitializing grid after restore"); String currentGridName = getCurrentGridName(context); String currentDbFile = dbFile; String newGridName = initGrid(context, currentGridName); String newDbFile = dbFile; if (!newDbFile.equals(currentDbFile)) { - Log.d(TAG, "Restored grid is disabled : " + currentGridName + FileLog.d(TAG, "Restored grid is disabled : " + currentGridName + ", migrating to: " + newGridName + ", removing all other grid db files"); for (String gridDbFile : LauncherFiles.GRID_DB_FILES) { @@ -288,7 +301,7 @@ public class InvariantDeviceProfile { continue; } if (context.getDatabasePath(gridDbFile).delete()) { - Log.d(TAG, "Removed old grid db file: " + gridDbFile); + FileLog.d(TAG, "Removed old grid db file: " + gridDbFile); } } setCurrentGrid(context, newGridName); @@ -302,7 +315,7 @@ public class InvariantDeviceProfile { int type = displayInfo.supportedBounds.stream() .mapToInt(bounds -> displayInfo.isTablet(bounds) ? flagTablet : flagPhone) .reduce(0, (a, b) -> a | b); - if ((type == (flagPhone | flagTablet)) && ENABLE_TWO_PANEL_HOME.get()) { + if (type == (flagPhone | flagTablet)) { // device has profiles supporting both phone and table modes return TYPE_MULTI_DISPLAY; } else if (type == flagTablet) { @@ -354,6 +367,13 @@ public class InvariantDeviceProfile { isScalable = closestProfile.isScalable; devicePaddingId = closestProfile.devicePaddingId; workspaceSpecsId = closestProfile.mWorkspaceSpecsId; + workspaceSpecsTwoPanelId = closestProfile.mWorkspaceSpecsTwoPanelId; + allAppsSpecsId = closestProfile.mAllAppsSpecsId; + allAppsSpecsTwoPanelId = closestProfile.mAllAppsSpecsTwoPanelId; + folderSpecsId = closestProfile.mFolderSpecsId; + folderSpecsTwoPanelId = closestProfile.mFolderSpecsTwoPanelId; + hotseatSpecsId = closestProfile.mHotseatSpecsId; + hotseatSpecsTwoPanelId = closestProfile.mHotseatSpecsTwoPanelId; this.deviceType = deviceType; inlineNavButtonsEndSpacing = closestProfile.inlineNavButtonsEndSpacing; @@ -800,6 +820,13 @@ public class InvariantDeviceProfile { private final boolean isScalable; private final int devicePaddingId; private final int mWorkspaceSpecsId; + private final int mWorkspaceSpecsTwoPanelId; + private final int mAllAppsSpecsId; + private final int mAllAppsSpecsTwoPanelId; + private final int mFolderSpecsId; + private final int mFolderSpecsTwoPanelId; + private final int mHotseatSpecsId; + private final int mHotseatSpecsTwoPanelId; public GridOption(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes( @@ -864,8 +891,33 @@ public class InvariantDeviceProfile { if (FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE.get()) { mWorkspaceSpecsId = a.getResourceId( R.styleable.GridDisplayOption_workspaceSpecsId, INVALID_RESOURCE_HANDLE); + mWorkspaceSpecsTwoPanelId = a.getResourceId( + R.styleable.GridDisplayOption_workspaceSpecsTwoPanelId, + INVALID_RESOURCE_HANDLE); + mAllAppsSpecsId = a.getResourceId( + R.styleable.GridDisplayOption_allAppsSpecsId, INVALID_RESOURCE_HANDLE); + mAllAppsSpecsTwoPanelId = a.getResourceId( + R.styleable.GridDisplayOption_allAppsSpecsTwoPanelId, + INVALID_RESOURCE_HANDLE); + mFolderSpecsId = a.getResourceId( + R.styleable.GridDisplayOption_folderSpecsId, INVALID_RESOURCE_HANDLE); + mFolderSpecsTwoPanelId = a.getResourceId( + R.styleable.GridDisplayOption_folderSpecsTwoPanelId, + INVALID_RESOURCE_HANDLE); + mHotseatSpecsId = a.getResourceId( + R.styleable.GridDisplayOption_hotseatSpecsId, INVALID_RESOURCE_HANDLE); + mHotseatSpecsTwoPanelId = a.getResourceId( + R.styleable.GridDisplayOption_hotseatSpecsTwoPanelId, + INVALID_RESOURCE_HANDLE); } else { mWorkspaceSpecsId = INVALID_RESOURCE_HANDLE; + mWorkspaceSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; + mAllAppsSpecsId = INVALID_RESOURCE_HANDLE; + mAllAppsSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; + mFolderSpecsId = INVALID_RESOURCE_HANDLE; + mFolderSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; + mHotseatSpecsId = INVALID_RESOURCE_HANDLE; + mHotseatSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; } int inlineForRotation = a.getInt(R.styleable.GridDisplayOption_inlineQsb, diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 66690431ec..038967c98f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -21,6 +21,7 @@ import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; import static android.content.pm.ActivityInfo.CONFIG_UI_MODE; import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; +import static com.android.app.animation.Interpolators.EMPHASIZED; import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_FOLDER; import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE; @@ -43,7 +44,6 @@ import static com.android.launcher3.LauncherState.NO_SCALE; import static com.android.launcher3.LauncherState.SPRING_LOADED; import static com.android.launcher3.Utilities.postAsyncCallback; import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED; import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE; import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE; import static com.android.launcher3.config.FeatureFlags.SHOW_DOT_PAGINATION; @@ -100,7 +100,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.os.Build; import android.os.Bundle; -import android.os.CancellationSignal; import android.os.Parcelable; import android.os.StrictMode; import android.os.SystemClock; @@ -145,7 +144,9 @@ import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.BaseSearchConfig; import com.android.launcher3.allapps.DiscoveryBounce; +import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.PropertyListBuilder; +import com.android.launcher3.apppairs.AppPairIcon; import com.android.launcher3.celllayout.CellPosMapper; import com.android.launcher3.celllayout.CellPosMapper.CellPos; import com.android.launcher3.celllayout.CellPosMapper.TwoPanelCellPosMapper; @@ -154,7 +155,6 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dot.DotInfo; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragLayer; -import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.dragndrop.LauncherDragController; import com.android.launcher3.folder.Folder; @@ -183,7 +183,6 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.pageindicators.WorkspacePageIndicator; import com.android.launcher3.pm.PinRequestHelper; -import com.android.launcher3.pm.UserCache; import com.android.launcher3.popup.ArrowPopup; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.PopupDataProvider; @@ -196,9 +195,11 @@ import com.android.launcher3.states.RotationHelper; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.touch.AllAppsSwipeController; +import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.ActivityResultInfo; import com.android.launcher3.util.ActivityTracker; +import com.android.launcher3.util.CannedAnimationCoordinator; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; @@ -208,7 +209,6 @@ import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.PendingRequestArgs; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.RunnableList; -import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.ScreenOnTracker; import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener; import com.android.launcher3.util.SystemUiController; @@ -411,8 +411,6 @@ public class Launcher extends StatefulActivity<LauncherState> protected long mLastTouchUpTime = -1; private boolean mTouchInProgress; - private SafeCloseable mUserChangedCallbackCloseable; - // New InstanceId is assigned to mAllAppsSessionLogId for each AllApps sessions. // When Launcher is not in AllApps state mAllAppsSessionLogId will be null. // User actions within AllApps state are logged with this InstanceId, to recreate AllApps @@ -425,6 +423,9 @@ public class Launcher extends StatefulActivity<LauncherState> private StartupLatencyLogger mStartupLatencyLogger; private CellPosMapper mCellPosMapper = CellPosMapper.DEFAULT; + private final CannedAnimationCoordinator mAnimationCoordinator = + new CannedAnimationCoordinator(this); + @Override @TargetApi(Build.VERSION_CODES.S) protected void onCreate(Bundle savedInstanceState) { @@ -445,8 +446,7 @@ public class Launcher extends StatefulActivity<LauncherState> Trace.beginAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME, DISPLAY_ALL_APPS_TRACE_COOKIE); } - Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT, - TraceHelper.FLAG_UI_EVENT); + TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT); if (DEBUG_STRICT_MODE) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads() @@ -580,10 +580,7 @@ public class Launcher extends StatefulActivity<LauncherState> LauncherOverlayPlugin.class, false /* allowedMultiple */); mRotationHelper.initialize(); - TraceHelper.INSTANCE.endSection(traceToken); - - mUserChangedCallbackCloseable = UserCache.INSTANCE.get(this).addUserChangeListener( - () -> getStateManager().goToState(NORMAL)); + TraceHelper.INSTANCE.endSection(); if (Utilities.ATLEAST_R) { getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING); @@ -733,18 +730,23 @@ public class Launcher extends StatefulActivity<LauncherState> @Override protected void onHandleConfigurationChanged() { - if (!initDeviceProfile(mDeviceProfile.inv)) { - return; - } + Trace.beginSection("Launcher#onHandleconfigurationChanged"); + try { + if (!initDeviceProfile(mDeviceProfile.inv)) { + return; + } - dispatchDeviceProfileChanged(); - reapplyUi(); - mDragLayer.recreateControllers(); + dispatchDeviceProfileChanged(); + reapplyUi(); + mDragLayer.recreateControllers(); - // Calling onSaveInstanceState ensures that static cache used by listWidgets is - // initialized properly. - onSaveInstanceState(new Bundle()); - mModel.rebindCallbacks(); + // Calling onSaveInstanceState ensures that static cache used by listWidgets is + // initialized properly. + onSaveInstanceState(new Bundle()); + mModel.rebindCallbacks(); + } finally { + Trace.endSection(); + } } public void onAssistantVisibilityChanged(float visibility) { @@ -1079,15 +1081,14 @@ public class Launcher extends StatefulActivity<LauncherState> @Override protected void onStart() { - Object traceToken = TraceHelper.INSTANCE.beginSection(ON_START_EVT, - TraceHelper.FLAG_UI_EVENT); + TraceHelper.INSTANCE.beginSection(ON_START_EVT); super.onStart(); if (!mDeferOverlayCallbacks) { mOverlayManager.onActivityStarted(this); } mAppWidgetHolder.setActivityStarted(true); - TraceHelper.INSTANCE.endSection(traceToken); + TraceHelper.INSTANCE.endSection(); } @Override @@ -1258,8 +1259,7 @@ public class Launcher extends StatefulActivity<LauncherState> @Override protected void onResume() { - Object traceToken = TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT, - TraceHelper.FLAG_UI_EVENT); + TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT); super.onResume(); if (mDeferOverlayCallbacks) { @@ -1269,7 +1269,7 @@ public class Launcher extends StatefulActivity<LauncherState> } DragView.removeAllViews(this); - TraceHelper.INSTANCE.endSection(traceToken); + TraceHelper.INSTANCE.endSection(); } @Override @@ -1402,9 +1402,10 @@ public class Launcher extends StatefulActivity<LauncherState> * @param info The data structure describing the shortcut. * @return A View inflated from layoutResId. */ - public View createShortcut(ViewGroup parent, WorkspaceItemInfo info) { - BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext()) - .inflate(R.layout.app_icon, parent, false); + public View createShortcut(@Nullable ViewGroup parent, WorkspaceItemInfo info) { + BubbleTextView favorite = + (BubbleTextView) LayoutInflater.from(parent != null ? parent.getContext() : this) + .inflate(R.layout.app_icon, parent, false); favorite.applyFromWorkspaceItem(info); favorite.setOnClickListener(getItemOnClickListener()); favorite.setOnFocusChangeListener(mFocusHandler); @@ -1418,8 +1419,7 @@ public class Launcher extends StatefulActivity<LauncherState> */ protected void completeAddShortcut(Intent data, int container, int screenId, int cellX, int cellY, PendingRequestArgs args) { - if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT - || args.getPendingIntent().getComponent() == null) { + if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT) { return; } @@ -1657,7 +1657,7 @@ public class Launcher extends StatefulActivity<LauncherState> if (Utilities.isRunningInTestHarness()) { Log.d(TestProtocol.PERMANENT_DIAG_TAG, "Launcher.onNewIntent: " + intent); } - Object traceToken = TraceHelper.INSTANCE.beginSection(ON_NEW_INTENT_EVT); + TraceHelper.INSTANCE.beginSection(ON_NEW_INTENT_EVT); super.onNewIntent(intent); boolean alreadyOnHome = hasWindowFocus() && ((intent.getFlags() & @@ -1673,7 +1673,8 @@ public class Launcher extends StatefulActivity<LauncherState> if (isActionMain) { if (!internalStateHandled) { // In all these cases, only animate if we're already on home - closeOpenViews(isStarted()); + AbstractFloatingView.closeAllOpenViewsExcept( + this, isStarted(), AbstractFloatingView.TYPE_LISTENER); if (!isInState(NORMAL)) { // Only change state, if not already the same. This prevents cancelling any @@ -1694,6 +1695,9 @@ public class Launcher extends StatefulActivity<LauncherState> if (mLauncherCallbacks != null) { mLauncherCallbacks.onHomeIntent(internalStateHandled); } + if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + handleSplitAnimationGoingToHome(); + } mOverlayManager.hideOverlay(isStarted() && !isForceInvisible()); handleGestureContract(intent); } else if (Intent.ACTION_ALL_APPS.equals(intent.getAction())) { @@ -1704,14 +1708,28 @@ public class Launcher extends StatefulActivity<LauncherState> showAllAppsWorkTabFromIntent(alreadyOnHome); } - TraceHelper.INSTANCE.endSection(traceToken); + TraceHelper.INSTANCE.endSection(); + } + + /** Handle animating away split placeholder view when user taps on home button */ + protected void handleSplitAnimationGoingToHome() { + // Overridden } protected void toggleAllAppsFromIntent(boolean alreadyOnHome) { if (getStateManager().isInStableState(ALL_APPS)) { getStateManager().goToState(NORMAL, alreadyOnHome); } else { - showAllAppsFromIntent(alreadyOnHome); + AbstractFloatingView.closeAllOpenViews(this); + getStateManager().goToState(ALL_APPS, true /* animated */, + new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + if (mAppsView.getSearchUiManager().getEditText() != null) { + mAppsView.getSearchUiManager().getEditText().requestFocus(); + } + } + }); } } @@ -1808,7 +1826,6 @@ public class Launcher extends StatefulActivity<LauncherState> LauncherAppState.getIDP(this).removeOnChangeListener(this); mOverlayManager.onActivityDestroyed(this); - mUserChangedCallbackCloseable.close(); } public LauncherAccessibilityDelegate getAccessibilityDelegate() { @@ -1928,16 +1945,10 @@ public class Launcher extends StatefulActivity<LauncherState> info.spanX = spanX; info.spanY = spanY; - switch (info.itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: - case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: - addAppWidgetFromDrop((PendingAddWidgetInfo) info); - break; - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: - processShortcutFromDrop((PendingAddShortcutInfo) info); - break; - default: - throw new IllegalStateException("Unknown item type: " + info.itemType); + if (info instanceof PendingAddWidgetInfo) { + addAppWidgetFromDrop((PendingAddWidgetInfo) info); + } else { // info can only be PendingAddShortcutInfo + processShortcutFromDrop((PendingAddShortcutInfo) info); } } @@ -2305,7 +2316,7 @@ public class Launcher extends StatefulActivity<LauncherState> * Implementation of the method from LauncherModel.Callbacks. */ public void startBinding() { - Object traceToken = TraceHelper.INSTANCE.beginSection("startBinding"); + TraceHelper.INSTANCE.beginSection("startBinding"); // Floating panels (except the full widget sheet) are associated with individual icons. If // we are starting a fresh bind, close all such panels as all the icons are about // to go away. @@ -2323,11 +2334,12 @@ public class Launcher extends StatefulActivity<LauncherState> if (mHotseat != null) { mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout()); } - TraceHelper.INSTANCE.endSection(traceToken); + TraceHelper.INSTANCE.endSection(); } @Override public void bindScreens(IntArray orderedScreenIds) { + mWorkspace.mPageIndicator.setAreScreensBinding(true); int firstScreenPosition = 0; if (FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != firstScreenPosition) { @@ -2345,13 +2357,37 @@ public class Launcher extends StatefulActivity<LauncherState> mWorkspace.unlockWallpaperFromDefaultPageOnNextLayout(); } + /** + * Remove odd number because they are already included when isTwoPanels and add the pair screen + * if not present. + */ + private IntArray filterTwoPanelScreenIds(IntArray orderedScreenIds) { + IntSet screenIds = IntSet.wrap(orderedScreenIds); + orderedScreenIds.forEach(screenId -> { + if (screenId % 2 == 1) { + screenIds.remove(screenId); + // In case the pair is not added, add it + if (!mWorkspace.containsScreenId(screenId - 1)) { + screenIds.add(screenId - 1); + } + } + }); + return screenIds.getArray(); + } + private void bindAddScreens(IntArray orderedScreenIds) { + if (mDeviceProfile.isTwoPanels) { - // Some empty pages might have been removed while the phone was in a single panel - // mode, so we want to add those empty pages back. - IntSet screenIds = IntSet.wrap(orderedScreenIds); - orderedScreenIds.forEach(screenId -> screenIds.add(mWorkspace.getScreenPair(screenId))); - orderedScreenIds = screenIds.getArray(); + if (FOLDABLE_SINGLE_PAGE.get()) { + orderedScreenIds = filterTwoPanelScreenIds(orderedScreenIds); + } else { + // Some empty pages might have been removed while the phone was in a single panel + // mode, so we want to add those empty pages back. + IntSet screenIds = IntSet.wrap(orderedScreenIds); + orderedScreenIds.forEach( + screenId -> screenIds.add(mWorkspace.getScreenPair(screenId))); + orderedScreenIds = screenIds.getArray(); + } } int count = orderedScreenIds.size(); @@ -2442,7 +2478,6 @@ public class Launcher extends StatefulActivity<LauncherState> final View view; switch (item.itemType) { case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: { WorkspaceItemInfo info = (WorkspaceItemInfo) item; view = createShortcut(info); @@ -2455,9 +2490,9 @@ public class Launcher extends StatefulActivity<LauncherState> break; } case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR: { - FolderInfo info = (FolderInfo) item; - // TODO (jeremysim b/274189428): Create app pair icon - view = null; + view = AppPairIcon.inflateIcon(R.layout.app_pair_icon, this, + (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()), + (FolderInfo) item); break; } case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: @@ -2577,7 +2612,7 @@ public class Launcher extends StatefulActivity<LauncherState> return view; } - Object traceToken = TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId); + TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId); try { final LauncherAppWidgetProviderInfo appWidgetInfo; @@ -2707,7 +2742,7 @@ public class Launcher extends StatefulActivity<LauncherState> } prepareAppWidget(view, item); } finally { - TraceHelper.INSTANCE.endSection(traceToken); + TraceHelper.INSTANCE.endSection(); } return view; @@ -2800,7 +2835,7 @@ public class Launcher extends StatefulActivity<LauncherState> * Implementation of the method from LauncherModel.Callbacks. */ public void finishBindingItems(IntSet pagesBoundFirst) { - Object traceToken = TraceHelper.INSTANCE.beginSection("finishBindingItems"); + TraceHelper.INSTANCE.beginSection("finishBindingItems"); mWorkspace.restoreInstanceStateForRemainingPages(); setWorkspaceLoading(false); @@ -2825,9 +2860,9 @@ public class Launcher extends StatefulActivity<LauncherState> mDeviceProfile.inv.numFolderColumns * mDeviceProfile.inv.numFolderRows); getViewCache().setCacheSize(R.layout.folder_page, 2); - TraceHelper.INSTANCE.endSection(traceToken); - - mWorkspace.removeExtraEmptyScreen(true); + TraceHelper.INSTANCE.endSection(); + mWorkspace.removeExtraEmptyScreen(/* stripEmptyScreens= */ true); + mWorkspace.mPageIndicator.setAreScreensBinding(false); } private boolean canAnimatePageChange() { @@ -2981,9 +3016,14 @@ public class Launcher extends StatefulActivity<LauncherState> public void bindAllApplications(AppInfo[] apps, int flags, Map<PackageUserKey, Integer> packageUserKeytoUidMap) { Preconditions.assertUIThread(); - AllAppsStore appsStore = mAppsView.getAppsStore(); + boolean hadWorkApps = mAppsView.shouldShowTabs(); + AllAppsStore<Launcher> appsStore = mAppsView.getAppsStore(); appsStore.setApps(apps, flags, packageUserKeytoUidMap); PopupContainerWithArrow.dismissInvalidPopup(this); + if (hadWorkApps != mAppsView.shouldShowTabs()) { + getStateManager().goToState(NORMAL); + } + if (Utilities.ATLEAST_S) { Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME, DISPLAY_ALL_APPS_TRACE_COOKIE); @@ -3055,6 +3095,7 @@ public class Launcher extends StatefulActivity<LauncherState> @Override public void bindStringCache(StringCache cache) { mStringCache = cache; + mAppsView.updateWorkUI(); } @Override @@ -3246,8 +3287,6 @@ public class Launcher extends StatefulActivity<LauncherState> return new TouchController[] {getDragController(), new AllAppsSwipeController(this)}; } - public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { } - public void onDragLayerHierarchyChanged() { updateDisallowBack(); } @@ -3313,10 +3352,6 @@ public class Launcher extends StatefulActivity<LauncherState> return false; } - public DragOptions getDefaultWorkspaceDragOptions() { - return new DragOptions(); - } - /** * Animates Launcher elements during a transition to the All Apps page. * @@ -3388,4 +3423,24 @@ public class Launcher extends StatefulActivity<LauncherState> public boolean areFreeformTasksVisible() { return false; // Base launcher does not track freeform tasks } + + @Override + public View.OnLongClickListener getAllAppsItemLongClickListener() { + return ItemLongClickListener.INSTANCE_ALL_APPS; + } + + /** + * Handles an app pair launch; overridden in + * {@link com.android.launcher3.uioverrides.QuickstepLauncher} + */ + public void launchAppPair(WorkspaceItemInfo app1, WorkspaceItemInfo app2) { + // Overridden + } + + /** + * Returns the animation coordinator for playing one-off animations + */ + public CannedAnimationCoordinator getAnimationCoordinator() { + return mAnimationCoordinator; + } } diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 4d15ac7452..9db8c82a03 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -17,6 +17,7 @@ package com.android.launcher3; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED; +import static android.content.Context.RECEIVER_EXPORTED; import static com.android.launcher3.LauncherPrefs.ICON_STATE; import static com.android.launcher3.LauncherPrefs.THEMED_ICONS; @@ -26,6 +27,7 @@ import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.pm.LauncherApps; @@ -38,7 +40,6 @@ import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.IconShape; import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.IconProvider; @@ -62,6 +63,7 @@ public class LauncherAppState implements SafeCloseable { public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher"; public static final String KEY_ICON_STATE = "pref_icon_shape_path"; + public static final String KEY_ALL_APPS_OVERVIEW_THRESHOLD = "pref_all_apps_overview_threshold"; // We do not need any synchronization for this variable as its only written on UI thread. public static final MainThreadInitializedObject<LauncherAppState> INSTANCE = @@ -104,27 +106,27 @@ public class LauncherAppState implements SafeCloseable { }); mContext.getSystemService(LauncherApps.class).registerCallback(mModel); + mOnTerminateCallback.add(() -> + mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel)); SimpleBroadcastReceiver modelChangeReceiver = new SimpleBroadcastReceiver(mModel::onBroadcastIntent); modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED, - Intent.ACTION_MANAGED_PROFILE_AVAILABLE, - Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, - Intent.ACTION_MANAGED_PROFILE_UNLOCKED, - Intent.ACTION_PROFILE_INACCESSIBLE, ACTION_DEVICE_POLICY_RESOURCE_UPDATED); - if (FeatureFlags.IS_STUDIO_BUILD) { - modelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD); + if (BuildConfig.IS_STUDIO_BUILD) { + mContext.registerReceiver(modelChangeReceiver, new IntentFilter(ACTION_FORCE_ROLOAD), + RECEIVER_EXPORTED); } mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver)); SafeCloseable userChangeListener = UserCache.INSTANCE.get(mContext) - .addUserChangeListener(mModel::forceReload); + .addUserEventListener(mModel::onUserEvent); mOnTerminateCallback.add(userChangeListener::close); LockedUserState.get(context).runOnUserUnlocked(() -> { - CustomWidgetManager.INSTANCE.get(mContext) - .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts); + CustomWidgetManager cwm = CustomWidgetManager.INSTANCE.get(mContext); + cwm.setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts); + mOnTerminateCallback.add(() -> cwm.setWidgetRefreshCallback(null)); IconObserver observer = new IconObserver(); SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener( @@ -159,6 +161,7 @@ public class LauncherAppState implements SafeCloseable { mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext), iconCacheFileName != null); mOnTerminateCallback.add(mIconCache::close); + mOnTerminateCallback.add(mModel::destroy); } private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) { @@ -180,9 +183,6 @@ public class LauncherAppState implements SafeCloseable { */ @Override public void close() { - mModel.destroy(); - mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel); - CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null); mOnTerminateCallback.executeAllAndDestroy(); } diff --git a/src/com/android/launcher3/LauncherBackupAgent.java b/src/com/android/launcher3/LauncherBackupAgent.java index 3d2700de40..2617b9337d 100644 --- a/src/com/android/launcher3/LauncherBackupAgent.java +++ b/src/com/android/launcher3/LauncherBackupAgent.java @@ -34,7 +34,7 @@ public class LauncherBackupAgent extends BackupAgent { // Remove old files which might contain obsolete attributes like idp_grid_name in shared // preference that will obstruct backup's attribute from writing to shared preferences. if (destination.delete()) { - FileLog.d("LauncherBackupAgent", "Removed obsolete file: " + destination); + FileLog.d(TAG, "onRestoreFile: Removed obsolete file " + destination); } super.onRestoreFile(data, size, destination, type, mode, mtime); } @@ -47,6 +47,7 @@ public class LauncherBackupAgent extends BackupAgent { @Override public void onRestoreFinished() { + FileLog.d(TAG, "onRestoreFinished: set pending for RestoreDbTask"); RestoreDbTask.setPending(this); } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 4e066b0985..fd8f66851d 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -20,9 +20,7 @@ import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURC import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD; import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD; -import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING; import static com.android.launcher3.testing.shared.TestProtocol.sDebugTracing; -import static com.android.launcher3.testing.shared.TestProtocol.testLogD; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; @@ -69,7 +67,6 @@ import com.android.launcher3.pm.InstallSessionTracker; import com.android.launcher3.pm.PackageInstallInfo; import com.android.launcher3.pm.UserCache; import com.android.launcher3.shortcuts.ShortcutRequest; -import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.PackageUserKey; @@ -95,15 +92,6 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi static final String TAG = "Launcher.Model"; - // Broadcast intent to track when the profile gets locked: - // ACTION_MANAGED_PROFILE_UNAVAILABLE can be used until Android U where profile no longer gets - // locked when paused. - // ACTION_PROFILE_INACCESSIBLE always means that the profile is getting locked but it only - // appeared in Android S. - private static final String ACTION_PROFILE_LOCKED = Utilities.ATLEAST_U - ? Intent.ACTION_PROFILE_INACCESSIBLE - : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE; - @NonNull private final LauncherAppState mApp; @NonNull @@ -114,6 +102,9 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi private LoaderTask mLoaderTask; private boolean mIsLoaderTaskRunning; + // only allow this once per reboot to reload work apps + private boolean mShouldReloadWorkProfile = true; + // Indicates whether the current model data is valid or not. // We start off with everything not loaded. After that, we assume that // our monitoring of the package manager provides all updates and we never @@ -143,6 +134,8 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi @NonNull private final ModelDelegate mModelDelegate; + private int mLastLoadId = -1; + // Runnable to check if the shortcuts permission has changed. @NonNull private final Runnable mDataValidationCheck = new Runnable() { @@ -301,28 +294,6 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { // If we have changed locale we need to clear out the labels in all apps/workspace. forceReload(); - } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) - || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) - || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action) - || Intent.ACTION_PROFILE_INACCESSIBLE.equals(action)) { - UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.WORK_TAB_MISSING, "onBroadcastIntent intentAction: " + action + - " user: " + user); - } - if (user != null) { - if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || - Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { - enqueueModelUpdateTask(new PackageUpdatedTask( - PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)); - } - - if (ACTION_PROFILE_LOCKED.equals(action) - || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) { - enqueueModelUpdateTask(new UserLockStateChangedTask( - user, Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action))); - } - } } else if (ACTION_DEVICE_POLICY_RESOURCE_UPDATED.equals(action)) { enqueueModelUpdateTask(new ReloadStringCacheTask(mModelDelegate)); } else if (IS_STUDIO_BUILD && ACTION_FORCE_ROLOAD.equals(action)) { @@ -335,6 +306,30 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } /** + * Called then there use a user event + * @see UserCache#addUserEventListener + */ + public void onUserEvent(UserHandle user, String action) { + if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) + && mShouldReloadWorkProfile) { + mShouldReloadWorkProfile = false; + forceReload(); + } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) + || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { + mShouldReloadWorkProfile = false; + enqueueModelUpdateTask(new PackageUpdatedTask( + PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)); + } else if (UserCache.ACTION_PROFILE_LOCKED.equals(action) + || UserCache.ACTION_PROFILE_UNLOCKED.equals(action)) { + enqueueModelUpdateTask(new UserLockStateChangedTask( + user, UserCache.ACTION_PROFILE_UNLOCKED.equals(action))); + } else if (UserCache.ACTION_PROFILE_ADDED.equals(action) + || UserCache.ACTION_PROFILE_REMOVED.equals(action)) { + forceReload(); + } + } + + /** * Reloads the workspace items from the DB and re-binds the workspace. This should generally * not be called as DB updates are automatically followed by UI update */ @@ -553,6 +548,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi if (mLoaderTask != task) { throw new CancellationException("Loader already stopped"); } + mLastLoadId++; mTask = task; mIsLoaderTaskRunning = true; mModelLoaded = false; @@ -563,7 +559,6 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi synchronized (mLock) { // Everything loaded bind the data. mModelLoaded = true; - testLogD(WORK_TAB_MISSING, "launcher model loaded"); } } @@ -722,4 +717,12 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi return mCallbacksList.toArray(new Callbacks[mCallbacksList.size()]); } } + + /** + * Returns the ID for the last model load. If the load ID doesn't match for a transaction, the + * transaction should be ignored. + */ + public int getLastLoadId() { + return mLastLoadId; + } } diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt index c98df1b099..427eaa3bab 100644 --- a/src/com/android/launcher3/LauncherPrefs.kt +++ b/src/com/android/launcher3/LauncherPrefs.kt @@ -275,6 +275,9 @@ class LauncherPrefs(private val encryptedContext: Context) { const val TASKBAR_PINNING_KEY = "TASKBAR_PINNING_KEY" @JvmField val ICON_STATE = nonRestorableItem(LauncherAppState.KEY_ICON_STATE, "", true) + @JvmField + val ALL_APPS_OVERVIEW_THRESHOLD = + nonRestorableItem(LauncherAppState.KEY_ALL_APPS_OVERVIEW_THRESHOLD, 180, true) @JvmField val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false, true) @JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "") @JvmField val WORK_EDU_STEP = backedUpItem(WorkProfileManager.KEY_WORK_EDU_STEP, 0) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 9abec505c7..440e1465ea 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -16,21 +16,18 @@ package com.android.launcher3; -import android.annotation.TargetApi; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.ContentProvider; -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; import android.content.ContentUris; import android.content.ContentValues; -import android.content.OperationApplicationException; import android.database.Cursor; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.os.Binder; -import android.os.Build; -import android.os.Bundle; import android.os.Process; import android.text.TextUtils; import android.util.Log; @@ -38,12 +35,11 @@ import android.util.Log; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.ModelDbController; -import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; import com.android.launcher3.widget.LauncherWidgetHolder; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayList; +import java.util.function.ToIntFunction; public class LauncherProvider extends ContentProvider { private static final String TAG = "LauncherProvider"; @@ -74,10 +70,6 @@ public class LauncherProvider extends ContentProvider { return true; } - public ModelDbController getModelDbController() { - return LauncherAppState.getInstance(getContext()).getModel().getModelDbController(); - } - @Override public String getType(Uri uri) { SqlArguments args = new SqlArguments(uri, null, null); @@ -91,180 +83,91 @@ public class LauncherProvider extends ContentProvider { @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { - SqlArguments args = new SqlArguments(uri, selection, selectionArgs); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(args.table); - Cursor result = getModelDbController().query( - args.table, projection, args.where, args.args, sortOrder); - result.setNotificationUri(getContext().getContentResolver(), uri); - return result; - } - - private void reloadLauncherIfExternal() { - if (Binder.getCallingPid() != Process.myPid()) { - LauncherAppState app = LauncherAppState.getInstanceNoCreate(); - if (app != null) { - app.getModel().forceReload(); - } - } + Cursor[] result = new Cursor[1]; + executeControllerTask(controller -> { + result[0] = controller.query(args.table, projection, args.where, args.args, sortOrder); + return 0; + }); + return result[0]; } @Override - public Uri insert(Uri uri, ContentValues initialValues) { - // In very limited cases, we support system|signature permission apps to modify the db. - if (Binder.getCallingPid() != Process.myPid()) { - if (!initializeExternalAdd(initialValues)) { - return null; - } - } - - SqlArguments args = new SqlArguments(uri); - int rowId = getModelDbController().insert(args.table, initialValues); - if (rowId < 0) return null; - - uri = ContentUris.withAppendedId(uri, rowId); - reloadLauncherIfExternal(); - return uri; - } - - private boolean initializeExternalAdd(ContentValues values) { - // 1. Ensure that externally added items have a valid item id - int id = getModelDbController().generateNewItemId(); - values.put(LauncherSettings.Favorites._ID, id); - - // 2. In the case of an app widget, and if no app widget id is specified, we - // attempt allocate and bind the widget. - Integer itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE); - if (itemType != null && - itemType.intValue() == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && - !values.containsKey(LauncherSettings.Favorites.APPWIDGET_ID)) { - - final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getContext()); - ComponentName cn = ComponentName.unflattenFromString( - values.getAsString(Favorites.APPWIDGET_PROVIDER)); + public Uri insert(Uri uri, ContentValues values) { + int rowId = executeControllerTask(controller -> { + // 1. Ensure that externally added items have a valid item id + int id = controller.generateNewItemId(); + values.put(LauncherSettings.Favorites._ID, id); + + // 2. In the case of an app widget, and if no app widget id is specified, we + // attempt allocate and bind the widget. + Integer itemType = values.getAsInteger(Favorites.ITEM_TYPE); + if (itemType != null + && itemType.intValue() == Favorites.ITEM_TYPE_APPWIDGET + && !values.containsKey(Favorites.APPWIDGET_ID)) { + + ComponentName cn = ComponentName.unflattenFromString( + values.getAsString(Favorites.APPWIDGET_PROVIDER)); + if (cn == null) { + return 0; + } - if (cn != null) { LauncherWidgetHolder widgetHolder = LauncherWidgetHolder.newInstance(getContext()); try { int appWidgetId = widgetHolder.allocateAppWidgetId(); values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId); - if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) { + if (!AppWidgetManager.getInstance(getContext()) + .bindAppWidgetIdIfAllowed(appWidgetId, cn)) { widgetHolder.deleteAppWidgetId(appWidgetId); - return false; + return 0; } } catch (RuntimeException e) { Log.e(TAG, "Failed to initialize external widget", e); - return false; + return 0; } finally { // Necessary to destroy the holder to free up possible activity context widgetHolder.destroy(); } - } else { - return false; } - } - - return true; - } - @Override - public int bulkInsert(Uri uri, ContentValues[] values) { - SqlArguments args = new SqlArguments(uri); - getModelDbController().bulkInsert(args.table, values); - reloadLauncherIfExternal(); - return values.length; - } + SqlArguments args = new SqlArguments(uri); + return controller.insert(args.table, values); + }); - @TargetApi(Build.VERSION_CODES.M) - @Override - public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) - throws OperationApplicationException { - try (SQLiteTransaction t = getModelDbController().newTransaction()) { - final int numOperations = operations.size(); - final ContentProviderResult[] results = new ContentProviderResult[numOperations]; - for (int i = 0; i < numOperations; i++) { - ContentProviderOperation op = operations.get(i); - results[i] = op.apply(this, results, i); - } - t.commit(); - reloadLauncherIfExternal(); - return results; - } + return rowId < 0 ? null : ContentUris.withAppendedId(uri, rowId); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SqlArguments args = new SqlArguments(uri, selection, selectionArgs); - int count = getModelDbController().delete(args.table, args.where, args.args); - if (count > 0) { - reloadLauncherIfExternal(); - } - return count; + return executeControllerTask(c -> c.delete(args.table, args.where, args.args)); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SqlArguments args = new SqlArguments(uri, selection, selectionArgs); - int count = getModelDbController().update(args.table, values, args.where, args.args); - reloadLauncherIfExternal(); - return count; + return executeControllerTask(c -> c.update(args.table, values, args.where, args.args)); } - @Override - public Bundle call(String method, final String arg, final Bundle extras) { - if (Binder.getCallingUid() != Process.myUid()) { - return null; + private int executeControllerTask(ToIntFunction<ModelDbController> task) { + if (Binder.getCallingPid() == Process.myPid()) { + throw new IllegalArgumentException("Same process should call model directly"); } - - switch (method) { - case LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG: { - getModelDbController().clearEmptyDbFlag(); - return null; - } - case LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS: { - Bundle result = new Bundle(); - result.putIntArray(LauncherSettings.Settings.EXTRA_VALUE, - getModelDbController().deleteEmptyFolders().toArray()); - return result; - } - case LauncherSettings.Settings.METHOD_NEW_ITEM_ID: { - Bundle result = new Bundle(); - result.putInt(LauncherSettings.Settings.EXTRA_VALUE, - getModelDbController().generateNewItemId()); - return result; - } - case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: { - Bundle result = new Bundle(); - result.putInt(LauncherSettings.Settings.EXTRA_VALUE, - getModelDbController().getNewScreenId()); - return result; - } - case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: { - getModelDbController().createEmptyDB(); - return null; - } - case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: { - getModelDbController().loadDefaultFavoritesIfNecessary(); - return null; - } - case LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS: { - getModelDbController().removeGhostWidgets(); - return null; - } - case LauncherSettings.Settings.METHOD_NEW_TRANSACTION: { - Bundle result = new Bundle(); - result.putBinder(LauncherSettings.Settings.EXTRA_VALUE, - getModelDbController().newTransaction()); - return result; - } - case LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE: { - getModelDbController().refreshHotseatRestoreTable(); - return null; - } + try { + return MODEL_EXECUTOR.submit(() -> { + LauncherModel model = LauncherAppState.getInstance(getContext()).getModel(); + int count = task.applyAsInt(model.getModelDbController()); + if (count > 0) { + MAIN_EXECUTOR.submit(model::forceReload); + } + return count; + }).get(); + } catch (Exception e) { + throw new IllegalStateException(e); } - return null; } static class SqlArguments { diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 7fda326ec2..105d5f30c8 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -16,10 +16,7 @@ package com.android.launcher3; -import android.content.ContentResolver; import android.database.sqlite.SQLiteDatabase; -import android.net.Uri; -import android.os.Bundle; import android.provider.BaseColumns; import com.android.launcher3.model.data.ItemInfo; @@ -89,7 +86,9 @@ public class LauncherSettings { /** * The gesture is an application created shortcut + * @deprecated This is no longer supported. Use {@link #ITEM_TYPE_DEEP_SHORTCUT} instead */ + @Deprecated public static final int ITEM_TYPE_SHORTCUT = 1; /** @@ -153,24 +152,6 @@ public class LauncherSettings { public static final String TMP_TABLE = "favorites_tmp"; /** - * The content:// style URL for "favorites" table - */ - public static final Uri CONTENT_URI = Uri.parse("content://" - + LauncherProvider.AUTHORITY + "/" + TABLE_NAME); - - /** - * The content:// style URL for a given row, identified by its id. - * - * @param id The row id. - * - * @return The unique content URL for the specified row. - */ - public static Uri getContentUri(int id) { - return Uri.parse("content://" + LauncherProvider.AUTHORITY - + "/" + TABLE_NAME + "/" + id); - } - - /** * The container holding the favorite * <P>Type: INTEGER</P> */ @@ -213,7 +194,6 @@ public class LauncherSettings { public static final String itemTypeToString(int type) { switch(type) { case ITEM_TYPE_APPLICATION: return "APP"; - case ITEM_TYPE_SHORTCUT: return "SHORTCUT"; case ITEM_TYPE_FOLDER: return "FOLDER"; case ITEM_TYPE_APPWIDGET: return "WIDGET"; case ITEM_TYPE_CUSTOM_APPWIDGET: return "CUSTOMWIDGET"; @@ -338,42 +318,8 @@ public class LauncherSettings { * Launcher settings */ public static final class Settings { - - public static final Uri CONTENT_URI = Uri.parse("content://" + - LauncherProvider.AUTHORITY + "/settings"); - - public static final String METHOD_CLEAR_EMPTY_DB_FLAG = "clear_empty_db_flag"; - - public static final String METHOD_DELETE_EMPTY_FOLDERS = "delete_empty_folders"; - - public static final String METHOD_NEW_ITEM_ID = "generate_new_item_id"; - public static final String METHOD_NEW_SCREEN_ID = "generate_new_screen_id"; - - public static final String METHOD_CREATE_EMPTY_DB = "create_empty_db"; - - public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites"; - - public static final String METHOD_REMOVE_GHOST_WIDGETS = "remove_ghost_widgets"; - - public static final String METHOD_NEW_TRANSACTION = "new_db_transaction"; - - public static final String METHOD_REFRESH_HOTSEAT_RESTORE_TABLE = "restore_hotseat_table"; - - public static final String EXTRA_VALUE = "value"; - - public static final String EXTRA_DB_NAME = "db_name"; - public static final String LAYOUT_DIGEST_KEY = "launcher3.layout.provider.blob"; public static final String LAYOUT_DIGEST_LABEL = "launcher-layout"; public static final String LAYOUT_DIGEST_TAG = "ignore"; - - public static Bundle call(ContentResolver cr, String method) { - return call(cr, method, null /* arg */); - } - - public static Bundle call(ContentResolver cr, String method, String arg) { - return cr.call(CONTENT_URI, method, arg, null); - } - } } diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 6097fb1947..d2ea7cc3f0 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -15,8 +15,8 @@ */ package com.android.launcher3; -import static com.android.launcher3.anim.Interpolators.ACCEL_2; -import static com.android.launcher3.anim.Interpolators.DEACCEL_2; +import static com.android.app.animation.Interpolators.ACCELERATE_2; +import static com.android.app.animation.Interpolators.DECELERATE_2; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW; import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL; @@ -33,6 +33,7 @@ import static com.android.launcher3.testing.shared.TestProtocol.SPRING_LOADED_ST import android.content.Context; import android.graphics.Color; +import android.view.View; import android.view.animation.Interpolator; import androidx.annotation.FloatRange; @@ -66,6 +67,7 @@ public abstract class LauncherState implements BaseState<LauncherState> { public static final int CLEAR_ALL_BUTTON = 1 << 4; public static final int WORKSPACE_PAGE_INDICATOR = 1 << 5; public static final int SPLIT_PLACHOLDER_VIEW = 1 << 6; + public static final int FLOATING_SEARCH_BAR = 1 << 7; // Flag indicating workspace has multiple pages visible. public static final int FLAG_MULTI_PAGE = BaseState.getFlag(0); @@ -90,7 +92,7 @@ public abstract class LauncherState implements BaseState<LauncherState> { public static final float NO_SCALE = 1; protected static final PageAlphaProvider DEFAULT_ALPHA_PROVIDER = - new PageAlphaProvider(ACCEL_2) { + new PageAlphaProvider(ACCELERATE_2) { @Override public float getPageAlpha(int pageIndex) { return 1; @@ -98,7 +100,7 @@ public abstract class LauncherState implements BaseState<LauncherState> { }; protected static final PageTranslationProvider DEFAULT_PAGE_TRANSLATION_PROVIDER = - new PageTranslationProvider(DEACCEL_2) { + new PageTranslationProvider(DECELERATE_2) { @Override public float getPageTranslation(int pageIndex) { return 0; @@ -202,8 +204,61 @@ public abstract class LauncherState implements BaseState<LauncherState> { return 0; } + /** + * How far from the bottom of the screen the <em>floating</em> search bar should rest in this + * state when the IME is not present. + * <p> + * To hide offscreen, use a negative value. + * <p> + * Note: if the provided value is non-negative but less than the current bottom insets, the + * insets will be applied. As such, you can use 0 to default to this. + */ + public int getFloatingSearchBarRestingMarginBottom(Launcher launcher) { + DeviceProfile dp = launcher.getDeviceProfile(); + return areElementsVisible(launcher, FLOATING_SEARCH_BAR) ? dp.getQsbOffsetY() + : -dp.hotseatQsbHeight; + } + + /** + * How far from the start of the screen the <em>floating</em> search bar should rest. + * <p> + * To use original margin, return a negative value. + */ + public int getFloatingSearchBarRestingMarginStart(Launcher launcher) { + boolean isRtl = Utilities.isRtl(launcher.getResources()); + View qsb = launcher.getHotseat().getQsb(); + return isRtl ? launcher.getHotseat().getRight() - qsb.getRight() : qsb.getLeft(); + } + + /** + * How far from the end of the screen the <em>floating</em> search bar should rest. + * <p> + * To use original margin, return a negative value. + */ + public int getFloatingSearchBarRestingMarginEnd(Launcher launcher) { + DeviceProfile dp = launcher.getDeviceProfile(); + if (dp.isQsbInline) { + int marginStart = getFloatingSearchBarRestingMarginStart(launcher); + return dp.widthPx - marginStart - dp.hotseatQsbWidth; + } + + boolean isRtl = Utilities.isRtl(launcher.getResources()); + View qsb = launcher.getHotseat().getQsb(); + return isRtl ? qsb.getLeft() : launcher.getHotseat().getRight() - qsb.getRight(); + } + + /** Whether the <em>floating</em> search bar should use the pill UI when not focused. */ + public boolean shouldFloatingSearchBarUsePillWhenUnfocused(Launcher launcher) { + return false; + } + public int getVisibleElements(Launcher launcher) { - return HOTSEAT_ICONS | WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR; + int elements = HOTSEAT_ICONS | WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR; + // Floating search bar is visible in normal state except in landscape on phones. + if (!(launcher.getDeviceProfile().isPhone && launcher.getDeviceProfile().isLandscape)) { + elements |= FLOATING_SEARCH_BAR; + } + return elements; } /** @@ -319,7 +374,7 @@ public abstract class LauncherState implements BaseState<LauncherState> { return DEFAULT_ALPHA_PROVIDER; } final int centerPage = launcher.getWorkspace().getNextPage(); - return new PageAlphaProvider(ACCEL_2) { + return new PageAlphaProvider(ACCELERATE_2) { @Override public float getPageAlpha(int pageIndex) { return pageIndex != centerPage ? 0 : 1f; @@ -336,7 +391,7 @@ public abstract class LauncherState implements BaseState<LauncherState> { return DEFAULT_PAGE_TRANSLATION_PROVIDER; } final float quarterPageSpacing = launcher.getWorkspace().getPageSpacing() / 4f; - return new PageTranslationProvider(DEACCEL_2) { + return new PageTranslationProvider(DECELERATE_2) { @Override public float getPageTranslation(int pageIndex) { boolean isRtl = launcher.getWorkspace().mIsRtl; diff --git a/src/com/android/launcher3/MotionEventsUtils.java b/src/com/android/launcher3/MotionEventsUtils.java index 40de003bd4..3228ec6942 100644 --- a/src/com/android/launcher3/MotionEventsUtils.java +++ b/src/com/android/launcher3/MotionEventsUtils.java @@ -30,6 +30,9 @@ public class MotionEventsUtils { /** {@link MotionEvent#CLASSIFICATION_MULTI_FINGER_SWIPE} is hidden. */ public static final int CLASSIFICATION_MULTI_FINGER_SWIPE = 4; + /** {@link MotionEvent#AXIS_GESTURE_SWIPE_FINGER_COUNT} is hidden. */ + private static final int AXIS_GESTURE_SWIPE_FINGER_COUNT = 53; + @TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public static boolean isTrackpadScroll(MotionEvent event) { return ENABLE_TRACKPAD_GESTURE.get() @@ -43,11 +46,13 @@ public class MotionEventsUtils { } public static boolean isTrackpadThreeFingerSwipe(MotionEvent event) { - return isTrackpadMultiFingerSwipe(event) && event.getPointerCount() == 3; + return isTrackpadMultiFingerSwipe(event) && event.getAxisValue( + AXIS_GESTURE_SWIPE_FINGER_COUNT) == 3; } public static boolean isTrackpadFourFingerSwipe(MotionEvent event) { - return isTrackpadMultiFingerSwipe(event) && event.getPointerCount() == 4; + return isTrackpadMultiFingerSwipe(event) && event.getAxisValue( + AXIS_GESTURE_SWIPE_FINGER_COUNT) == 4; } public static boolean isTrackpadMotionEvent(MotionEvent event) { diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index af64b3b0ee..4b4a4a5204 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -16,7 +16,7 @@ package com.android.launcher3; -import static com.android.launcher3.anim.Interpolators.SCROLL; +import static com.android.app.animation.Interpolators.SCROLL; import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType; import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR; diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java index aaccb7dfc0..d460ba8668 100644 --- a/src/com/android/launcher3/SessionCommitReceiver.java +++ b/src/com/android/launcher3/SessionCommitReceiver.java @@ -32,6 +32,8 @@ import com.android.launcher3.model.ItemInstallQueue; import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.util.Executors; +import java.util.Locale; + /** * BroadcastReceiver to handle session commit intent. */ @@ -63,9 +65,20 @@ public class SessionCommitReceiver extends BroadcastReceiver { } InstallSessionHelper packageInstallerCompat = InstallSessionHelper.INSTANCE.get(context); + boolean alreadyAddedPromiseIcon = + packageInstallerCompat.promiseIconAddedForId(info.getSessionId()); if (TextUtils.isEmpty(info.getAppPackageName()) || info.getInstallReason() != PackageManager.INSTALL_REASON_USER - || packageInstallerCompat.promiseIconAddedForId(info.getSessionId())) { + || alreadyAddedPromiseIcon) { + FileLog.d(LOG, + String.format(Locale.ENGLISH, + "Removing PromiseIcon for package: %s, install reason: %d," + + " alreadyAddedPromiseIcon: %s", + info.getAppPackageName(), + info.getInstallReason(), + alreadyAddedPromiseIcon + ) + ); packageInstallerCompat.removePromiseIconId(info.getSessionId()); return; } diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java index a0ceefb733..f0fea61b33 100644 --- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java +++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java @@ -28,6 +28,7 @@ import android.content.Context; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; +import android.os.Trace; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -65,6 +66,7 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. mActivity = ActivityContext.lookupContext(context); mWallpaperManager = WallpaperManager.getInstance(context); mContainerType = containerType; + setClipChildren(false); } public void setCellDimensions(int cellWidth, int cellHeight, int countX, int countY, @@ -154,14 +156,15 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. mBorderSpace); // Center the icon/folder int cHeight = getCellContentHeight(); - int cellPaddingY = dp.isScalableGrid && mContainerType == WORKSPACE - ? dp.cellYPaddingPx - : (int) Math.max(0, ((lp.height - cHeight) / 2f)); + int cellPaddingY = + dp.cellYPaddingPx >= 0 && mContainerType == WORKSPACE + ? dp.cellYPaddingPx + : (int) Math.max(0, ((lp.height - cHeight) / 2f)); // No need to add padding when cell layout border spacing is present. boolean noPaddingX = (dp.cellLayoutBorderSpacePx.x > 0 && mContainerType == WORKSPACE) - || (dp.folderCellLayoutBorderSpacePx > 0 && mContainerType == FOLDER) + || (dp.folderCellLayoutBorderSpacePx.x > 0 && mContainerType == FOLDER) || (dp.hotseatBorderSpace > 0 && mContainerType == HOTSEAT); int cellPaddingX = noPaddingX ? 0 @@ -181,6 +184,7 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { + Trace.beginSection("ShortcutAndWidgetConteiner#onLayout"); int count = getChildCount(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); @@ -188,6 +192,7 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. layoutChild(child); } } + Trace.endSection(); } /** diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 709c57c82e..e8c6ff9be7 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -24,6 +24,7 @@ import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MA import android.annotation.TargetApi; import android.app.ActivityManager; +import android.app.ActivityOptions; import android.app.Person; import android.app.WallpaperManager; import android.content.Context; @@ -333,14 +334,12 @@ public final class Utilities { public static void scaleRectAboutCenter(Rect r, float scale) { if (scale != 1.0f) { - int cx = r.centerX(); - int cy = r.centerY(); - r.offset(-cx, -cy); - r.left = (int) (r.left * scale + 0.5f); - r.top = (int) (r.top * scale + 0.5f); - r.right = (int) (r.right * scale + 0.5f); - r.bottom = (int) (r.bottom * scale + 0.5f); - r.offset(cx, cy); + float cx = r.exactCenterX(); + float cy = r.exactCenterY(); + r.left = Math.round(cx + (r.left - cx) * scale); + r.top = Math.round(cy + (r.top - cy) * scale); + r.right = Math.round(cx + (r.right - cx) * scale); + r.bottom = Math.round(cy + (r.bottom - cy) * scale); } } @@ -561,6 +560,17 @@ public final class Utilities { } /** + * Utility method to allow background activity launch for the provided activity options + */ + public static ActivityOptions allowBGLaunch(ActivityOptions options) { + if (ATLEAST_U) { + options.setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); + } + return options; + } + + /** * Returns the full drawable for info without any flattening or pre-processing. * * @param shouldThemeIcon If true, will theme icons when applicable @@ -569,12 +579,13 @@ public final class Utilities { */ @TargetApi(Build.VERSION_CODES.TIRAMISU) public static Drawable getFullDrawable(Context context, ItemInfo info, int width, int height, - boolean shouldThemeIcon, Object[] outObj) { + boolean shouldThemeIcon, Object[] outObj, boolean[] outIsIconThemed) { Drawable icon = loadFullDrawableWithoutTheme(context, info, width, height, outObj); if (ATLEAST_T && icon instanceof AdaptiveIconDrawable && shouldThemeIcon) { AdaptiveIconDrawable aid = (AdaptiveIconDrawable) icon.mutate(); Drawable mono = aid.getMonochrome(); if (mono != null && Themes.isThemedIconEnabled(context)) { + outIsIconThemed[0] = true; int[] colors = ThemedIconDrawable.getColors(context); mono = mono.mutate(); mono.setTint(colors[1]); @@ -635,7 +646,8 @@ public final class Utilities { * badge. When dragged from workspace or folder, it may contain app AND/OR work profile badge **/ @TargetApi(Build.VERSION_CODES.O) - public static Drawable getBadge(Context context, ItemInfo info, Object obj) { + public static Drawable getBadge(Context context, ItemInfo info, Object obj, + boolean isIconThemed) { LauncherAppState appState = LauncherAppState.getInstance(context); if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { boolean iconBadged = (info instanceof ItemInfoWithIcon) @@ -653,7 +665,8 @@ public final class Utilities { } else { return Process.myUserHandle().equals(info.user) ? new ColorDrawable(Color.TRANSPARENT) - : context.getDrawable(R.drawable.ic_work_app_badge); + : context.getDrawable(isIconThemed + ? R.drawable.ic_work_app_badge_themed : R.drawable.ic_work_app_badge); } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 73bb828317..8be8fedf80 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -67,9 +67,9 @@ import android.widget.Toast; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.android.app.animation.Interpolators; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.celllayout.CellLayoutLayoutParams; import com.android.launcher3.celllayout.CellPosMapper; @@ -562,9 +562,9 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> // Change the interpolators such that the fade animation plays before the move animation. // This prevents empty adjacent pages to overlay during animation mLayoutTransition.setInterpolator(LayoutTransition.DISAPPEARING, - Interpolators.clampToProgress(Interpolators.ACCEL_DEACCEL, 0, 0.5f)); + Interpolators.clampToProgress(Interpolators.ACCELERATE_DECELERATE, 0, 0.5f)); mLayoutTransition.setInterpolator(LayoutTransition.CHANGE_DISAPPEARING, - Interpolators.clampToProgress(Interpolators.ACCEL_DEACCEL, 0.5f, 1)); + Interpolators.clampToProgress(Interpolators.ACCELERATE_DECELERATE, 0.5f, 1)); mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING); mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING); @@ -731,6 +731,14 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> }); } + + /** + * Returns if the given screenId is already in the Workspace + */ + public boolean containsScreenId(int screenId) { + return this.mWorkspaceScreens.containsKey(screenId); + } + /** * Inserts extra empty pages to the end of the existing workspaces. * Usually we add one extra empty screen, but when two panel home is enabled we add @@ -897,9 +905,8 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> mWorkspaceScreens.remove(emptyScreenId); mScreenOrder.removeValue(emptyScreenId); - int newScreenId = LauncherSettings.Settings.call(getContext().getContentResolver(), - LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) - .getInt(LauncherSettings.Settings.EXTRA_VALUE); + int newScreenId = LauncherAppState.getInstance(getContext()) + .getModel().getModelDbController().getNewScreenId(); // Launcher database isn't aware of empty pages that are already bound, so we need to // skip those IDs manually. while (mWorkspaceScreens.containsKey(newScreenId)) { @@ -1840,7 +1847,6 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION); boolean willBecomeShortcut = (info.itemType == ITEM_TYPE_APPLICATION || - info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT); return (aboveShortcut && willBecomeShortcut); @@ -2759,7 +2765,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) info; boolean findNearestVacantCell = true; - if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { + if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY, cellLayout, mTargetCell); float distance = cellLayout.getDistanceFromWorkspaceCellVisualCenter( @@ -2832,8 +2838,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> View view; switch (info.itemType) { - case ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: + case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION: if (info instanceof WorkspaceItemFactory) { diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java index 4768773813..c6c38fcddb 100644 --- a/src/com/android/launcher3/WorkspaceLayoutManager.java +++ b/src/com/android/launcher3/WorkspaceLayoutManager.java @@ -55,6 +55,7 @@ public interface WorkspaceLayoutManager { int y = presenterPos.cellY; if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT || info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) { + Log.d(TAG, "add predicted icon " + child.getTag().toString() + " to home screen"); int screenId = presenterPos.screenId; x = getHotseat().getCellXFromOrder(screenId); y = getHotseat().getCellYFromOrder(screenId); diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 565d7da9e5..e4f34ae0ab 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -18,6 +18,9 @@ package com.android.launcher3; import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE; +import static com.android.app.animation.Interpolators.ACCELERATE_2; +import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.ZOOM_OUT; import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY; import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_WORKSPACE_STATE; import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; @@ -30,12 +33,8 @@ import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.WORKSPACE_PAGE_INDICATOR; -import static com.android.launcher3.anim.Interpolators.ACCEL_2; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.ZOOM_OUT; import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS; -import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS; import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE; @@ -54,6 +53,7 @@ import android.view.animation.Interpolator; import com.android.launcher3.LauncherState.PageAlphaProvider; import com.android.launcher3.LauncherState.PageTranslationProvider; import com.android.launcher3.LauncherState.ScaleAndTranslation; +import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.anim.SpringAnimationBuilder; @@ -196,12 +196,12 @@ public class WorkspaceStateTransitionAnimation { state.getWorkspaceBackgroundAlpha(mLauncher), LINEAR); SysUiScrim sysUiScrim = mLauncher.getRootView().getSysUiScrim(); - propertySetter.setFloat(sysUiScrim, SYSUI_PROGRESS, + propertySetter.setFloat(sysUiScrim.getSysUIProgress(), AnimatedFloat.VALUE, state.hasFlag(FLAG_HAS_SYS_UI_SCRIM) ? 1 : 0, LINEAR); propertySetter.setViewBackgroundColor(mLauncher.getScrimView(), state.getWorkspaceScrimColor(mLauncher), - config.getInterpolator(ANIM_SCRIM_FADE, ACCEL_2)); + config.getInterpolator(ANIM_SCRIM_FADE, ACCELERATE_2)); } public void applyChildState(LauncherState state, CellLayout cl, int childIndex) { diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index a7a25f4dce..758bffbeb8 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -7,6 +7,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE; +import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE; import android.appwidget.AppWidgetProviderInfo; import android.graphics.Point; @@ -124,12 +125,19 @@ public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate<Lau } } - if ((item instanceof WorkspaceItemFactory) || (item instanceof WorkspaceItemInfo) - || (item instanceof PendingAddItemInfo)) { + if (supportAddToWorkSpace(item)) { out.add(mActions.get(ADD_TO_WORKSPACE)); } } + private boolean supportAddToWorkSpace(ItemInfo item) { + return (item instanceof WorkspaceItemFactory) + || ((item instanceof WorkspaceItemInfo) + && (((WorkspaceItemInfo) item).runtimeStatusFlags & FLAG_NOT_PINNABLE) == 0) + || ((item instanceof PendingAddItemInfo) + && (((PendingAddItemInfo) item).runtimeStatusFlags & FLAG_NOT_PINNABLE) == 0); + } + /** * Returns all the accessibility actions that can be handled by the host. */ diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index 1977704588..40382b2a45 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -16,10 +16,14 @@ package com.android.launcher3.allapps; import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH; +import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD; +import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD; +import static com.android.launcher3.config.FeatureFlags.ALL_APPS_GONE_VISIBILITY; +import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_RV_PREINFLATION; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_COUNT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB; -import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE; import android.animation.Animator; @@ -69,13 +73,12 @@ import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem; -import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider; +import com.android.launcher3.allapps.search.AllAppsSearchUiDelegate; import com.android.launcher3.allapps.search.SearchAdapterProvider; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.keyboard.FocusedItemDecorator; import com.android.launcher3.model.StringCache; import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ActivityContext; @@ -131,12 +134,13 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> protected final Point mFastScrollerOffset = new Point(); protected final int mScrimColor; protected final float mHeaderThreshold; + protected final AllAppsSearchUiDelegate mSearchUiDelegate; // Used to animate Search results out and A-Z apps in, or vice-versa. private final SearchTransitionController mSearchTransitionController; private final Paint mHeaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Rect mInsets = new Rect(); - private final AllAppsStore mAllAppsStore = new AllAppsStore(); + private final AllAppsStore<T> mAllAppsStore; private final RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() { @Override @@ -189,6 +193,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> public ActivityAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mActivityContext = ActivityContext.lookupContext(context); + mAllAppsStore = new AllAppsStore<>(mActivityContext); mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor); mHeaderThreshold = getResources().getDimensionPixelSize( @@ -203,10 +208,6 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> mNavBarScrimPaint.setColor(Themes.getNavBarScrimColor(mActivityContext)); AllAppsStore.OnUpdateListener onAppsUpdated = this::onAppsUpdated; - if (TestProtocol.sDebugTracing) { - Log.d(WORK_TAB_MISSING, "ActivityAllAppsContainer#init registeringListener: " + - onAppsUpdated); - } mAllAppsStore.addUpdateListener(onAppsUpdated); // This is a focus listener that proxies focus from a view into the list view. This is to @@ -216,11 +217,21 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> getActiveRecyclerView().requestFocus(); } }); + mSearchUiDelegate = createSearchUiDelegate(); initContent(); mSearchTransitionController = new SearchTransitionController(this); } + /** Creates the delegate for initializing search. */ + protected AllAppsSearchUiDelegate createSearchUiDelegate() { + return new AllAppsSearchUiDelegate(this); + } + + public AllAppsSearchUiDelegate getSearchUiDelegate() { + return mSearchUiDelegate; + } + /** * Initializes the view hierarchy and internal variables. Any initialization which actually uses * these members should be done in {@link #onFinishInflate()}. @@ -230,7 +241,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> * onFinishInflate -> onPostCreate */ protected void initContent() { - mMainAdapterProvider = createMainAdapterProvider(); + mMainAdapterProvider = mSearchUiDelegate.createMainAdapterProvider(); mAH.set(AdapterHolder.MAIN, new AdapterHolder(AdapterHolder.MAIN, new AlphabeticalAppsList<>(mActivityContext, mAllAppsStore, null))); @@ -247,9 +258,12 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> mFastScroller = findViewById(R.id.fast_scroller); mFastScroller.setPopupView(findViewById(R.id.fast_scroller_popup)); - // Add the search box above everything else. - mSearchContainer = inflateSearchBox(); - addView(mSearchContainer); + mSearchContainer = inflateSearchBar(); + if (!isSearchBarFloating()) { + // Add the search box above everything else in this container (if the flag is enabled, + // it's added to drag layer in onAttach instead). + addView(mSearchContainer); + } mSearchUiManager = (SearchUiManager) mSearchContainer; } @@ -280,6 +294,13 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + if (isSearchBarFloating()) { + // Note: for Taskbar this is removed in TaskbarAllAppsController#cleanUpOverlay when the + // panel is closed. Can't do so in onDetach because we are also a child of drag layer + // so can't remove its views during that dispatch. + mActivityContext.getDragLayer().addView(mSearchContainer); + mSearchUiDelegate.onInitializeSearchBar(); + } mActivityContext.addOnDeviceProfileChangeListener(this); } @@ -301,7 +322,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> * Temporarily force the bottom sheet to be visible on non-tablets. * * @param force {@code true} means bottom sheet will be visible on phones until {@code reset()}. - **/ + */ public void forceBottomSheetVisible(boolean force) { mForceBottomSheetVisible = force; updateBackgroundVisibility(mActivityContext.getDeviceProfile()); @@ -339,6 +360,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> */ public void setSearchResults(ArrayList<AdapterItem> results, int searchResultCode) { setSearchResults(results); + mSearchUiDelegate.onSearchResultsChanged(results, searchResultCode); } private void animateToSearchState(boolean goingToSearch) { @@ -371,7 +393,10 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> rebindAdapters(false); mRebindAdaptersAfterSearchAnimation = false; } - if (!goingToSearch) { + + if (goingToSearch) { + mSearchUiDelegate.onAnimateToSearchStateCompleted(); + } else { setSearchResults(null); if (mViewPager != null) { mViewPager.setCurrentPage(previousPage); @@ -410,7 +435,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> * A-Z apps list. * * @param animate Whether to animate the header during the reset (e.g. switching profile tabs). - **/ + */ public void reset(boolean animate) { reset(animate, true); } @@ -420,7 +445,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> * * @param animate Whether to animate the header during the reset (e.g. switching profile tabs). * @param exitSearch Whether to force exit the search state and return to A-Z apps list. - **/ + */ public void reset(boolean animate, boolean exitSearch) { for (int i = 0; i < mAH.size(); i++) { if (mAH.get(i).mRecyclerView != null) { @@ -438,7 +463,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> updateHeaderScroll(0); if (exitSearch) { // Reset the search bar after transitioning home. - mSearchUiManager.resetSearch(); + MAIN_EXECUTOR.getHandler().post(mSearchUiManager::resetSearch); // Animate to A-Z with 0 time to reset the animation with proper state management. animateToSearchState(false, 0); } @@ -483,6 +508,9 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> // Will be called at the end of the animation. return; } + if (currentActivePage != SEARCH) { + mActivityContext.hideKeyboard(); + } if (mAH.get(currentActivePage).mRecyclerView != null) { mAH.get(currentActivePage).mRecyclerView.bindFastScrollbar(mFastScroller); } @@ -525,10 +553,14 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView); mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView); + final AllAppsRecyclerView mainRecyclerView; + final AllAppsRecyclerView workRecyclerView; if (mUsingTabs) { - mAH.get(AdapterHolder.MAIN).setup(mViewPager.getChildAt(0), mPersonalMatcher); - mAH.get(AdapterHolder.WORK).setup(mViewPager.getChildAt(1), mWorkManager.getMatcher()); - mAH.get(AdapterHolder.WORK).mRecyclerView.setId(R.id.apps_list_view_work); + mainRecyclerView = (AllAppsRecyclerView) mViewPager.getChildAt(0); + workRecyclerView = (AllAppsRecyclerView) mViewPager.getChildAt(1); + mAH.get(AdapterHolder.MAIN).setup(mainRecyclerView, mPersonalMatcher); + mAH.get(AdapterHolder.WORK).setup(workRecyclerView, mWorkManager.getMatcher()); + workRecyclerView.setId(R.id.apps_list_view_work); if (FeatureFlags.ENABLE_EXPANDING_PAUSE_WORK_BUTTON.get()) { mAH.get(AdapterHolder.WORK).mRecyclerView.addOnScrollListener( mWorkManager.newScrollListener()); @@ -540,7 +572,6 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> mActivityContext.getStatsLogManager().logger() .log(LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB); } - mActivityContext.hideKeyboard(); }); findViewById(R.id.tab_work) .setOnClickListener((View view) -> { @@ -548,24 +579,30 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> mActivityContext.getStatsLogManager().logger() .log(LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB); } - mActivityContext.hideKeyboard(); }); setDeviceManagementResources(); - onActivePageChanged(mViewPager.getNextPage()); + if (mHeader.isSetUp()) { + onActivePageChanged(mViewPager.getNextPage()); + } } else { - mAH.get(AdapterHolder.MAIN).setup(findViewById(R.id.apps_list_view), null); + mainRecyclerView = findViewById(R.id.apps_list_view); + workRecyclerView = null; + mAH.get(AdapterHolder.MAIN).setup(mainRecyclerView, null); mAH.get(AdapterHolder.WORK).mRecyclerView = null; } + setUpCustomRecyclerViewPool( + mainRecyclerView, + workRecyclerView, + mAllAppsStore.getRecyclerViewPool()); setupHeader(); - if (isSearchBarOnBottom()) { + if (isSearchBarFloating()) { // Keep the scroller above the search bar. RelativeLayout.LayoutParams scrollerLayoutParams = (LayoutParams) mFastScroller.getLayoutParams(); - scrollerLayoutParams.addRule(RelativeLayout.ABOVE, R.id.search_container_all_apps); - scrollerLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM); - scrollerLayoutParams.bottomMargin = getResources().getDimensionPixelSize( - R.dimen.fastscroll_bottom_margin_floating_search); + scrollerLayoutParams.bottomMargin = mSearchContainer.getHeight() + + getResources().getDimensionPixelSize( + R.dimen.fastscroll_bottom_margin_floating_search); } mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView); @@ -573,6 +610,30 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView); } + /** + * If {@link ENABLE_ALL_APPS_RV_PREINFLATION} is enabled, wire custom + * {@link RecyclerView.RecycledViewPool} to main and work {@link AllAppsRecyclerView}. + * + * Then if {@link ALL_APPS_GONE_VISIBILITY} is enabled, update max pool size. This is because + * all apps rv's hidden visibility is changed to {@link View#GONE} from {@link View#INVISIBLE), + * thus we cannot rely on layout pass to update pool size. + */ + private static void setUpCustomRecyclerViewPool( + @NonNull AllAppsRecyclerView mainRecyclerView, + @Nullable AllAppsRecyclerView workRecyclerView, + @NonNull RecyclerView.RecycledViewPool recycledViewPool) { + if (!ENABLE_ALL_APPS_RV_PREINFLATION.get()) { + return; + } + mainRecyclerView.setRecycledViewPool(recycledViewPool); + if (workRecyclerView != null) { + workRecyclerView.setRecycledViewPool(recycledViewPool); + } + if (ALL_APPS_GONE_VISIBILITY.get()) { + mainRecyclerView.updatePoolSize(); + } + } + private void replaceAppsRVContainer(boolean showTabs) { for (int i = AdapterHolder.MAIN; i <= AdapterHolder.WORK; i++) { AdapterHolder adapterHolder = mAH.get(i); @@ -617,11 +678,9 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> removeCustomRules(getSearchRecyclerView()); if (!isSearchSupported()) { layoutWithoutSearchContainer(rvContainer, showTabs); - } else if (isSearchBarOnBottom()) { + } else if (isSearchBarFloating()) { alignParentTop(rvContainer, showTabs); alignParentTop(getSearchRecyclerView(), /* tabs= */ false); - layoutAboveSearchContainer(rvContainer); - layoutAboveSearchContainer(getSearchRecyclerView()); } else { layoutBelowSearchContainer(rvContainer, showTabs); layoutBelowSearchContainer(getSearchRecyclerView(), /* tabs= */ false); @@ -652,7 +711,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> removeCustomRules(mHeader); if (!isSearchSupported()) { layoutWithoutSearchContainer(mHeader, false /* includeTabsMargin */); - } else if (isSearchBarOnBottom()) { + } else if (isSearchBarFloating()) { alignParentTop(mHeader, false /* includeTabsMargin */); } else { layoutBelowSearchContainer(mHeader, false /* includeTabsMargin */); @@ -692,16 +751,62 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> } /** - * It is up to the search container view created by {@link #inflateSearchBox()} to use the - * floating search bar flag to move itself to the bottom of this container. This method checks - * if that had been done; otherwise the flag will be ignored. - * - * @return true if the search bar is at the bottom of the container (as opposed to the top). - **/ - private boolean isSearchBarOnBottom() { - return FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() - && ((RelativeLayout.LayoutParams) mSearchContainer.getLayoutParams()).getRule( - ALIGN_PARENT_BOTTOM) == RelativeLayout.TRUE; + * @return true if the search bar is floating above this container (at the bottom of the screen) + */ + protected boolean isSearchBarFloating() { + return mSearchUiDelegate.isSearchBarFloating(); + } + + /** + * Whether the <em>floating</em> search bar should appear as a small pill when not focused. + * <p> + * Note: This method mirrors one in LauncherState. For subclasses that use Launcher, it likely + * makes sense to use that method to derive an appropriate value for the current/target state. + */ + public boolean shouldFloatingSearchBarBePillWhenUnfocused() { + return false; + } + + /** + * How far from the bottom of the screen the <em>floating</em> search bar should rest when the + * IME is not present. + * <p> + * To hide offscreen, use a negative value. + * <p> + * Note: if the provided value is non-negative but less than the current bottom insets, the + * insets will be applied. As such, you can use 0 to default to this. + * <p> + * Note: This method mirrors one in LauncherState. For subclasses that use Launcher, it likely + * makes sense to use that method to derive an appropriate value for the current/target state. + */ + public int getFloatingSearchBarRestingMarginBottom() { + return 0; + } + + /** + * How far from the start of the screen the <em>floating</em> search bar should rest. + * <p> + * To use original margin, return a negative value. + * <p> + * Note: This method mirrors one in LauncherState. For subclasses that use Launcher, it likely + * makes sense to use that method to derive an appropriate value for the current/target state. + */ + public int getFloatingSearchBarRestingMarginStart() { + DeviceProfile dp = mActivityContext.getDeviceProfile(); + return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin(); + } + + /** + * How far from the end of the screen the <em>floating</em> search bar should rest. + * <p> + * To use original margin, return a negative value. + * <p> + * Note: This method mirrors one in LauncherState. For subclasses that use Launcher, it likely + * makes sense to use that method to derive an appropriate value for the current/target state. + */ + public int getFloatingSearchBarRestingMarginEnd() { + DeviceProfile dp = mActivityContext.getDeviceProfile(); + return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin(); } private void layoutBelowSearchContainer(View v, boolean includeTabsMargin) { @@ -721,15 +826,6 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> layoutParams.topMargin = topMargin; } - private void layoutAboveSearchContainer(View v) { - if (!(v.getLayoutParams() instanceof RelativeLayout.LayoutParams)) { - return; - } - - RelativeLayout.LayoutParams layoutParams = (LayoutParams) v.getLayoutParams(); - layoutParams.addRule(RelativeLayout.ABOVE, R.id.search_container_all_apps); - } - private void alignParentTop(View v, boolean includeTabsMargin) { if (!(v.getLayoutParams() instanceof RelativeLayout.LayoutParams)) { return; @@ -783,15 +879,10 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> } /** - * Inflates the search box + * Inflates the search bar */ - protected View inflateSearchBox() { - return getLayoutInflater().inflate(R.layout.search_container_all_apps, this, false); - } - - /** Creates the adapter provider for the main section. */ - protected SearchAdapterProvider<?> createMainAdapterProvider() { - return new DefaultSearchAdapterProvider(mActivityContext); + protected View inflateSearchBar() { + return mSearchUiDelegate.inflateSearchBar(); } /** The adapter provider for the main section. */ @@ -831,16 +922,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> container.put(R.id.work_tab_state_id, state); } - /** - * Sets the long click listener for icons - */ - public void setOnIconLongClickListener(OnLongClickListener listener) { - for (AdapterHolder holder : mAH) { - holder.mAdapter.setOnIconLongClickListener(listener); - } - } - - public AllAppsStore getAppsStore() { + public AllAppsStore<T> getAppsStore() { return mAllAppsStore; } @@ -852,6 +934,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> public void onDeviceProfileChanged(DeviceProfile dp) { for (AdapterHolder holder : mAH) { holder.mAdapter.setAppsPerRow(dp.numShownAllAppsColumns); + holder.mAppsList.setNumAppsPerRowAllApps(dp.numShownAllAppsColumns); if (holder.mRecyclerView != null) { // Remove all views and clear the pool, while keeping the data same. After this // call, all the viewHolders will be recreated. @@ -883,10 +966,6 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> private void onAppsUpdated() { mHasWorkApps = Stream.of(mAllAppsStore.getApps()).anyMatch(mWorkManager.getMatcher()); - if (TestProtocol.sDebugTracing) { - Log.d(WORK_TAB_MISSING, "ActivityAllAppsContainerView#onAppsUpdated hasWorkApps: " + - mHasWorkApps + " allApps: " + mAllAppsStore.getApps().length); - } if (!isSearching()) { rebindAdapters(); if (mHasWorkApps) { @@ -971,7 +1050,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> /** * The container for A-Z apps (the ViewPager for main+work tabs, or main RV). This is currently * hidden while searching. - **/ + */ public ViewGroup getAppsRecyclerViewContainer() { return mViewPager != null ? mViewPager : findViewById(R.id.apps_list_view); } @@ -996,7 +1075,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> } public LayoutInflater getLayoutInflater() { - return LayoutInflater.from(getContext()); + return mSearchUiDelegate.getLayoutInflater(); } @Override @@ -1018,7 +1097,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0); } else { int topPadding = grid.allAppsTopPadding; - if (isSearchBarOnBottom() && !grid.isTablet) { + if (isSearchBarFloating() && !grid.isTablet) { topPadding += getResources().getDimensionPixelSize( R.dimen.all_apps_additional_top_padding_floating_search); } @@ -1094,7 +1173,10 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> } } - protected boolean shouldShowTabs() { + /** + * Returns true if the container has work apps. + */ + public boolean shouldShowTabs() { return mHasWorkApps; } @@ -1108,6 +1190,30 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> return view.getGlobalVisibleRect(new Rect()); } + /** Called in Launcher#bindStringCache() to update the UI when cache is updated. */ + public void updateWorkUI() { + setDeviceManagementResources(); + if (mWorkManager.getWorkModeSwitch() != null) { + mWorkManager.getWorkModeSwitch().updateStringFromCache(); + } + inflateWorkCardsIfNeeded(); + } + + private void inflateWorkCardsIfNeeded() { + AllAppsRecyclerView workRV = mAH.get(AdapterHolder.WORK).mRecyclerView; + if (workRV != null) { + for (int i = 0; i < workRV.getChildCount(); i++) { + View currentView = workRV.getChildAt(i); + int currentItemViewType = workRV.getChildViewHolder(currentView).getItemViewType(); + if (currentItemViewType == VIEW_TYPE_WORK_EDU_CARD) { + ((WorkEduCard) currentView).updateStringFromCache(); + } else if (currentItemViewType == VIEW_TYPE_WORK_DISABLED_CARD) { + ((WorkPausedCard) currentView).updateStringFromCache(); + } + } + } + } + @VisibleForTesting public boolean isPersonalTabVisible() { return isDescendantViewVisible(R.id.tab_personal); @@ -1230,7 +1336,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> final FloatingHeaderView headerView = getFloatingHeaderView(); if (hasBottomSheet) { // Start adding header protection if search bar or tabs will attach to the top. - if (!FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() || mUsingTabs) { + if (!isSearchBarFloating() || mUsingTabs) { mTmpRectF.set( leftWithScale, topWithScale, @@ -1286,7 +1392,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> /** Returns the position of the bottom edge of the header */ public int getHeaderBottom() { int bottom = (int) getTranslationY() + mHeader.getClipTop(); - if (isSearchBarOnBottom()) { + if (isSearchBarFloating()) { if (mActivityContext.getDeviceProfile().isTablet) { return bottom + mBottomSheetBackground.getTop(); } @@ -1304,6 +1410,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> protected void onInitializeRecyclerView(RecyclerView rv) { rv.addOnScrollListener(mScrollListener); + mSearchUiDelegate.onInitializeRecyclerView(rv); } /** Returns the instance of @{code SearchTransitionController}. */ @@ -1356,6 +1463,9 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> if (isWork() && mWorkManager.getWorkModeSwitch() != null) { bottomOffset = mInsets.bottom + mWorkManager.getWorkModeSwitch().getHeight(); } + if (isSearchBarFloating()) { + bottomOffset += mSearchContainer.getHeight(); + } mRecyclerView.setPadding(mPadding.left, mPadding.top, mPadding.right, mPadding.bottom + bottomOffset); } diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index 7c5c003915..7edbeac71f 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.allapps; +import static com.android.launcher3.config.FeatureFlags.ALL_APPS_GONE_VISIBILITY; import static com.android.launcher3.logger.LauncherAtom.ContainerInfo; import static com.android.launcher3.logger.LauncherAtom.SearchResultContainer; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_DOWN; @@ -26,6 +27,8 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_FAB_BUTTON_COLLAPSE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_FAB_BUTTON_EXTEND; +import static com.android.launcher3.recyclerview.AllAppsRecyclerViewPoolKt.EXTRA_ICONS_COUNT; +import static com.android.launcher3.recyclerview.AllAppsRecyclerViewPoolKt.PREINFLATE_ICONS_ROW_COUNT; import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING; import android.content.Context; @@ -96,8 +99,18 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx); pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1); pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER, 1); - pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows - * (mNumAppsPerRow + 1)); + + // If all apps' hidden visibility is INVISIBLE, we will need to preinflate one page of + // all apps icons for smooth scrolling. + int maxPoolSizeForAppIcons = (approxRows + 1) * grid.numShownAllAppsColumns; + if (ALL_APPS_GONE_VISIBILITY.get()) { + // If all apps' hidden visibility is GONE, we need to increase prefinated icons number + // by [PREINFLATE_ICONS_ROW_COUNT] rows + [EXTRA_ICONS_COUNT] for fast opening all apps. + maxPoolSizeForAppIcons += + PREINFLATE_ICONS_ROW_COUNT * grid.numShownAllAppsColumns + EXTRA_ICONS_COUNT; + } + pool.setMaxRecycledViews( + AllAppsGridAdapter.VIEW_TYPE_ICON, maxPoolSizeForAppIcons); } @Override diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java index 06af970cfc..e724858a79 100644 --- a/src/com/android/launcher3/allapps/AllAppsStore.java +++ b/src/com/android/launcher3/allapps/AllAppsStore.java @@ -15,24 +15,27 @@ */ package com.android.launcher3.allapps; +import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_RV_PREINFLATION; import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR; import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK; -import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING; +import android.content.Context; import android.os.UserHandle; -import android.util.Log; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView.RecycledViewPool; import com.android.launcher3.BubbleTextView; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.testing.shared.TestProtocol; +import com.android.launcher3.recyclerview.AllAppsRecyclerViewPool; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.views.ActivityContext; import java.util.ArrayList; import java.util.Arrays; @@ -45,8 +48,10 @@ import java.util.function.Predicate; /** * A utility class to maintain the collection of all apps. + * + * @param <T> The type of the context. */ -public class AllAppsStore { +public class AllAppsStore<T extends Context & ActivityContext> { // Defer updates flag used to defer all apps updates to the next draw. public static final int DEFER_UPDATES_NEXT_DRAW = 1 << 0; @@ -56,7 +61,7 @@ public class AllAppsStore { private PackageUserKey mTempKey = new PackageUserKey(null, null); private AppInfo mTempInfo = new AppInfo(); - private AppInfo[] mApps = EMPTY_ARRAY; + private @NonNull AppInfo[] mApps = EMPTY_ARRAY; private final List<OnUpdateListener> mUpdateListeners = new CopyOnWriteArrayList<>(); private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>(); @@ -64,20 +69,36 @@ public class AllAppsStore { private int mModelFlags; private int mDeferUpdatesFlags = 0; private boolean mUpdatePending = false; + private final AllAppsRecyclerViewPool mAllAppsRecyclerViewPool = new AllAppsRecyclerViewPool(); + + private final T mContext; public AppInfo[] getApps() { return mApps; } + public AllAppsStore(@NonNull T context) { + mContext = context; + } + /** * Sets the current set of apps and sets mapping for {@link PackageUserKey} to Uid for * the current set of apps. */ - public void setApps(AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map) { - mApps = apps; + public void setApps(@Nullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map) { + mApps = apps == null ? EMPTY_ARRAY : apps; mModelFlags = flags; notifyUpdate(); mPackageUserKeytoUidMap = map; + // Preinflate all apps RV when apps has changed, which can happen after unlocking screen, + // rotating screen, or downloading/upgrading apps. + if (ENABLE_ALL_APPS_RV_PREINFLATION.get()) { + mAllAppsRecyclerViewPool.preInflateAllAppsViewHolders(mContext); + } + } + + RecycledViewPool getRecyclerViewPool() { + return mAllAppsRecyclerViewPool; } /** @@ -134,9 +155,6 @@ public class AllAppsStore { return; } for (OnUpdateListener listener : mUpdateListeners) { - if (TestProtocol.sDebugTracing) { - Log.d(WORK_TAB_MISSING, "AllAppsStore#notifyUpdate listener: " + listener); - } listener.onAppsUpdated(); } } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index d4f152aa0a..c09a5b9d93 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -15,14 +15,15 @@ */ package com.android.launcher3.allapps; +import static com.android.app.animation.Interpolators.DECELERATE_1_7; +import static com.android.app.animation.Interpolators.INSTANT; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT; +import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7; -import static com.android.launcher3.anim.Interpolators.INSTANT; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; @@ -45,6 +46,7 @@ import android.view.animation.Interpolator; import androidx.annotation.FloatRange; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.Launcher; @@ -53,7 +55,6 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.anim.AnimatorListeners; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.config.FeatureFlags; @@ -234,7 +235,11 @@ public class AllAppsTransitionController */ public void setProgress(float progress) { mProgress = progress; - getAppsViewProgressTranslationY().setValue(mProgress * mShiftRange); + boolean fromBackground = + mLauncher.getStateManager().getCurrentStableState() == BACKGROUND_APP; + // Allow apps panel to shift the full screen if coming from another app. + float shiftRange = fromBackground ? mLauncher.getDeviceProfile().heightPx : mShiftRange; + getAppsViewProgressTranslationY().setValue(mProgress * shiftRange); mLauncher.onAllAppsTransition(1 - progress); boolean hasScrim = progress < NAV_BAR_COLOR_FORCE_UPDATE_THRESHOLD @@ -380,7 +385,7 @@ public class AllAppsTransitionController // need to decide depending on the release velocity Interpolator verticalProgressInterpolator = config.getInterpolator(ANIM_VERTICAL_PROGRESS, - config.userControlled ? LINEAR : DEACCEL_1_7); + config.userControlled ? LINEAR : DECELERATE_1_7); Animator anim = createSpringAnimation(mProgress, targetProgress); anim.setInterpolator(verticalProgressInterpolator); anim.addListener(getProgressAnimatorListener()); @@ -433,7 +438,8 @@ public class AllAppsTransitionController mAppsView = appsView; mAppsView.setScrimView(scrimView); - mAppsViewAlpha = new MultiValueAlpha(mAppsView, APPS_VIEW_INDEX_COUNT); + mAppsViewAlpha = new MultiValueAlpha(mAppsView, APPS_VIEW_INDEX_COUNT, + FeatureFlags.ALL_APPS_GONE_VISIBILITY.get() ? View.GONE : View.INVISIBLE); mAppsViewAlpha.setUpdateVisibility(true); mAppsViewTranslationY = new MultiPropertyFactory<>( mAppsView, VIEW_TRANSLATE_Y, APPS_VIEW_INDEX_COUNT, Float::sum); diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionListener.java b/src/com/android/launcher3/allapps/AllAppsTransitionListener.java new file mode 100644 index 0000000000..4a17e29039 --- /dev/null +++ b/src/com/android/launcher3/allapps/AllAppsTransitionListener.java @@ -0,0 +1,37 @@ +/* + * 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.launcher3.allapps; + +/** + * An interface for listening to all-apps open-close transition + */ +public interface AllAppsTransitionListener { + /** + * Called when the transition starts + * @param toAllApps {@code true} if this transition is supposed to end in the AppApps UI + * + * @see ActivityAllAppsContainerView + */ + void onAllAppsTransitionStart(boolean toAllApps); + + /** + * Called when the transition ends + * @param toAllApps {@code true} if the final state is all-apps + * + * @see ActivityAllAppsContainerView + */ + void onAllAppsTransitionEnd(boolean toAllApps); +} diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 29767bf465..ee4b5bc5f9 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -69,7 +69,7 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement // The set of apps from the system private final List<AppInfo> mApps = new ArrayList<>(); @Nullable - private final AllAppsStore mAllAppsStore; + private final AllAppsStore<T> mAllAppsStore; // The number of results in current adapter private int mAccessibilityResultsCount = 0; @@ -82,22 +82,27 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement private final ArrayList<AdapterItem> mSearchResults = new ArrayList<>(); private BaseAllAppsAdapter<T> mAdapter; private AppInfoComparator mAppNameComparator; - private final int mNumAppsPerRowAllApps; + private int mNumAppsPerRowAllApps; private int mNumAppRowsInAdapter; private Predicate<ItemInfo> mItemFilter; - public AlphabeticalAppsList(Context context, @Nullable AllAppsStore appsStore, + public AlphabeticalAppsList(Context context, @Nullable AllAppsStore<T> appsStore, WorkProfileManager workProfileManager) { mAllAppsStore = appsStore; mActivityContext = ActivityContext.lookupContext(context); mAppNameComparator = new AppInfoComparator(context); mWorkProviderManager = workProfileManager; - mNumAppsPerRowAllApps = mActivityContext.getDeviceProfile().inv.numAllAppsColumns; + mNumAppsPerRowAllApps = mActivityContext.getDeviceProfile().numShownAllAppsColumns; if (mAllAppsStore != null) { mAllAppsStore.addUpdateListener(this); } } + /** Set the number of apps per row when device profile changes. */ + public void setNumAppsPerRowAllApps(int numAppsPerRow) { + mNumAppsPerRowAllApps = numAppsPerRow; + } + public void updateItemFilter(Predicate<ItemInfo> itemFilter) { this.mItemFilter = itemFilter; onAppsUpdated(); diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java index 8fa42765b0..be0a898531 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java @@ -15,10 +15,7 @@ */ package com.android.launcher3.allapps; -import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APPS; - import android.content.Context; -import android.content.res.Resources; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -27,13 +24,12 @@ import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.widget.TextView; -import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.BubbleTextView; import com.android.launcher3.R; -import com.android.launcher3.allapps.search.SearchAdapterProvider; import com.android.launcher3.Utilities; +import com.android.launcher3.allapps.search.SearchAdapterProvider; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.views.ActivityContext; @@ -139,31 +135,24 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex protected final LayoutInflater mLayoutInflater; protected final OnClickListener mOnIconClickListener; - protected OnLongClickListener mOnIconLongClickListener = INSTANCE_ALL_APPS; + protected final OnLongClickListener mOnIconLongClickListener; protected OnFocusChangeListener mIconFocusListener; private final int mExtraTextHeight; public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater, AlphabeticalAppsList<T> apps, SearchAdapterProvider<?> adapterProvider) { - Resources res = activityContext.getResources(); mActivityContext = activityContext; mApps = apps; mLayoutInflater = inflater; mOnIconClickListener = mActivityContext.getItemOnClickListener(); + mOnIconLongClickListener = mActivityContext.getAllAppsItemLongClickListener(); mAdapterProvider = adapterProvider; mExtraTextHeight = Utilities.calculateTextHeight( mActivityContext.getDeviceProfile().allAppsIconTextSizePx); } - /** - * Sets the long click listener for icons - */ - public void setOnIconLongClickListener(@Nullable OnLongClickListener listener) { - mOnIconLongClickListener = listener; - } - /** Checks if the passed viewType represents all apps divider. */ public static boolean isDividerViewType(int viewType) { return isViewType(viewType, VIEW_TYPE_MASK_DIVIDER); diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java index aefedae641..5e48177d75 100644 --- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java @@ -22,6 +22,7 @@ import android.view.WindowInsets; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; +import com.android.launcher3.statemanager.StateManager; /** * AllAppsContainerView with launcher specific callbacks @@ -53,4 +54,87 @@ public class LauncherAllAppsContainerView extends ActivityAllAppsContainerView<L public boolean isInAllApps() { return mActivityContext.getStateManager().isInStableState(LauncherState.ALL_APPS); } + + @Override + public boolean shouldFloatingSearchBarBePillWhenUnfocused() { + if (!isSearchBarFloating()) { + return false; + } + Launcher launcher = mActivityContext; + StateManager<LauncherState> manager = launcher.getStateManager(); + if (manager.isInTransition() && manager.getTargetState() != null) { + return manager.getTargetState().shouldFloatingSearchBarUsePillWhenUnfocused(launcher); + } + return manager.getCurrentStableState() + .shouldFloatingSearchBarUsePillWhenUnfocused(launcher); + } + + @Override + public int getFloatingSearchBarRestingMarginBottom() { + if (!isSearchBarFloating()) { + return super.getFloatingSearchBarRestingMarginBottom(); + } + Launcher launcher = mActivityContext; + StateManager<LauncherState> stateManager = launcher.getStateManager(); + + // We want to rest at the current state's resting position, unless we are in transition and + // the target state's resting position is higher (that way if we are closing the keyboard, + // we can stop translating at that point). + int currentStateMarginBottom = stateManager.getCurrentStableState() + .getFloatingSearchBarRestingMarginBottom(launcher); + int targetStateMarginBottom = -1; + if (stateManager.isInTransition() && stateManager.getTargetState() != null) { + targetStateMarginBottom = stateManager.getTargetState() + .getFloatingSearchBarRestingMarginBottom(launcher); + if (targetStateMarginBottom < 0) { + // Go ahead and move offscreen. + return targetStateMarginBottom; + } + } + return Math.max(targetStateMarginBottom, currentStateMarginBottom); + } + + @Override + public int getFloatingSearchBarRestingMarginStart() { + if (!isSearchBarFloating()) { + return super.getFloatingSearchBarRestingMarginStart(); + } + + StateManager<LauncherState> stateManager = mActivityContext.getStateManager(); + + // Special case to not expand the search bar when exiting All Apps on phones. + if (stateManager.getCurrentStableState() == LauncherState.ALL_APPS + && mActivityContext.getDeviceProfile().isPhone) { + return LauncherState.ALL_APPS.getFloatingSearchBarRestingMarginStart(mActivityContext); + } + + if (stateManager.isInTransition() && stateManager.getTargetState() != null) { + return stateManager.getTargetState() + .getFloatingSearchBarRestingMarginStart(mActivityContext); + } + return stateManager.getCurrentStableState() + .getFloatingSearchBarRestingMarginStart(mActivityContext); + } + + @Override + public int getFloatingSearchBarRestingMarginEnd() { + if (!isSearchBarFloating()) { + return super.getFloatingSearchBarRestingMarginEnd(); + } + + StateManager<LauncherState> stateManager = mActivityContext.getStateManager(); + + // Special case to not expand the search bar when exiting All Apps on phones. + if (stateManager.getCurrentStableState() == LauncherState.ALL_APPS + && mActivityContext.getDeviceProfile().isPhone) { + return LauncherState.ALL_APPS.getFloatingSearchBarRestingMarginEnd(mActivityContext); + } + + if (stateManager.isInTransition() && stateManager.getTargetState() != null) { + return stateManager.getTargetState() + .getFloatingSearchBarRestingMarginEnd(mActivityContext); + } + return stateManager.getCurrentStableState() + .getFloatingSearchBarRestingMarginEnd(mActivityContext); + } } diff --git a/src/com/android/launcher3/allapps/SearchTransitionController.java b/src/com/android/launcher3/allapps/SearchTransitionController.java index b01ea53621..eb1bc0a4be 100644 --- a/src/com/android/launcher3/allapps/SearchTransitionController.java +++ b/src/com/android/launcher3/allapps/SearchTransitionController.java @@ -20,12 +20,12 @@ import static android.view.View.VISIBLE; import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; +import static com.android.app.animation.Interpolators.DECELERATE_1_7; +import static com.android.app.animation.Interpolators.INSTANT; +import static com.android.app.animation.Interpolators.clampToProgress; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback; -import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7; -import static com.android.launcher3.anim.Interpolators.INSTANT; -import static com.android.launcher3.anim.Interpolators.clampToProgress; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; @@ -48,7 +48,7 @@ public class SearchTransitionController { private static final String LOG_TAG = "SearchTransitionCtrl"; // Interpolator when the user taps the QSB while already in All Apps. - private static final Interpolator INTERPOLATOR_WITHIN_ALL_APPS = DEACCEL_1_7; + private static final Interpolator INTERPOLATOR_WITHIN_ALL_APPS = DECELERATE_1_7; // Interpolator when the user taps the QSB from home screen, so transition to all apps is // happening simultaneously. private static final Interpolator INTERPOLATOR_TRANSITIONING_TO_ALL_APPS = INSTANT; diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java index 2174936654..bfd89678df 100644 --- a/src/com/android/launcher3/allapps/SearchUiManager.java +++ b/src/com/android/launcher3/allapps/SearchUiManager.java @@ -49,6 +49,14 @@ public interface SearchUiManager { ExtendedEditText getEditText(); /** + * Hint to the edit text that it is about to be focused or unfocused. This can be used to start + * animating the edit box accordingly, e.g. after a gesture completes. + * + * @param focused true if the edit text is about to be focused, false if it will be unfocused + */ + default void prepareToFocusEditText(boolean focused) {} + + /** * Sets whether EditText background should be visible * @param maxAlpha defines the maximum alpha the background should animates to */ diff --git a/src/com/android/launcher3/allapps/WorkEduCard.java b/src/com/android/launcher3/allapps/WorkEduCard.java index b4cdc967dc..1059097c5a 100644 --- a/src/com/android/launcher3/allapps/WorkEduCard.java +++ b/src/com/android/launcher3/allapps/WorkEduCard.java @@ -76,11 +76,7 @@ public class WorkEduCard extends FrameLayout implements super.onFinishInflate(); findViewById(R.id.action_btn).setOnClickListener(this); - StringCache cache = mActivityContext.getStringCache(); - if (cache != null) { - TextView title = findViewById(R.id.work_apps_paused_title); - title.setText(cache.workProfileEdu); - } + updateStringFromCache(); } @Override @@ -121,4 +117,12 @@ public class WorkEduCard extends FrameLayout implements public void setPosition(int position) { mPosition = position; } + + public void updateStringFromCache() { + StringCache cache = mActivityContext.getStringCache(); + if (cache != null) { + TextView title = findViewById(R.id.work_apps_paused_title); + title.setText(cache.workProfileEdu); + } + } } diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java index 8c2fb195c6..144381c9c1 100644 --- a/src/com/android/launcher3/allapps/WorkModeSwitch.java +++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java @@ -17,7 +17,6 @@ package com.android.launcher3.allapps; import static com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.getTabWidth; -import android.animation.LayoutTransition; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; @@ -36,7 +35,6 @@ import com.android.launcher3.Insettable; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.KeyboardInsetAnimationCallback; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.StringCache; import com.android.launcher3.views.ActivityContext; @@ -92,12 +90,7 @@ public class WorkModeSwitch extends LinearLayout implements Insettable, } setInsets(mActivityContext.getDeviceProfile().getInsets()); - StringCache cache = mActivityContext.getStringCache(); - if (cache != null) { - mTextView.setText(cache.workProfilePauseButton); - } - - getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); + updateStringFromCache(); } @Override @@ -108,7 +101,7 @@ public class WorkModeSwitch extends LinearLayout implements Insettable, if (lp != null) { int bottomMargin = getResources().getDimensionPixelSize(R.dimen.work_fab_margin_bottom); DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile(); - if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) { + if (mActivityContext.getAppsView().isSearchBarFloating()) { bottomMargin += dp.hotseatQsbHeight; } @@ -213,4 +206,11 @@ public class WorkModeSwitch extends LinearLayout implements Insettable, public int getScrollThreshold() { return mScrollThreshold; } + + public void updateStringFromCache(){ + StringCache cache = mActivityContext.getStringCache(); + if (cache != null) { + mTextView.setText(cache.workProfilePauseButton); + } + } } diff --git a/src/com/android/launcher3/allapps/WorkPausedCard.java b/src/com/android/launcher3/allapps/WorkPausedCard.java index 26a78039cb..18826673a1 100644 --- a/src/com/android/launcher3/allapps/WorkPausedCard.java +++ b/src/com/android/launcher3/allapps/WorkPausedCard.java @@ -57,6 +57,10 @@ public class WorkPausedCard extends LinearLayout implements View.OnClickListener mBtn = findViewById(R.id.enable_work_apps); mBtn.setOnClickListener(this); + updateStringFromCache(); + } + + public void updateStringFromCache() { StringCache cache = mActivityContext.getStringCache(); if (cache != null) { setWorkProfilePausedResources(cache); diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java index 44c233f557..1ac8d87a77 100644 --- a/src/com/android/launcher3/allapps/WorkProfileManager.java +++ b/src/com/android/launcher3/allapps/WorkProfileManager.java @@ -25,7 +25,6 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; -import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import android.os.Build; @@ -47,7 +46,6 @@ import com.android.launcher3.Utilities; import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip; import java.lang.annotation.Retention; @@ -144,10 +142,6 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP } private void updateCurrentState(@WorkProfileState int currentState) { - if (TestProtocol.sDebugTracing) { - Log.d(WORK_TAB_MISSING, "WorkProfileManager#updateCurrentState: " + - currentState, new Throwable()); - } mCurrentState = currentState; if (getAH() != null) { getAH().mAppsList.updateAdapterItems(); @@ -166,10 +160,6 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP * Creates and attaches for profile toggle button to {@link ActivityAllAppsContainerView} */ public boolean attachWorkModeSwitch() { - if (TestProtocol.sDebugTracing) { - Log.d(WORK_TAB_MISSING, "ActivityAllAppsContainerView#attachWorkModeSwitch " - + "mWorkModeSwitch: " + mWorkModeSwitch); - } if (!mAllApps.getAppsStore().hasModelFlag( FLAG_HAS_SHORTCUT_PERMISSION | FLAG_QUIET_MODE_CHANGE_PERMISSION)) { Log.e(TAG, "unable to attach work mode switch; Missing required permissions"); diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java index 4427a49dab..ecbc7a94b3 100644 --- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java +++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java @@ -160,7 +160,7 @@ public class AllAppsSearchBarController * Focuses the search field to handle key events. */ public void focusSearchField() { - mInput.showKeyboard(); + mInput.showKeyboard(true /* shouldFocus */); } /** diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchUiDelegate.java b/src/com/android/launcher3/allapps/search/AllAppsSearchUiDelegate.java new file mode 100644 index 0000000000..2347bfd5a7 --- /dev/null +++ b/src/com/android/launcher3/allapps/search/AllAppsSearchUiDelegate.java @@ -0,0 +1,86 @@ +/* + * 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.launcher3.allapps.search; + +import android.view.LayoutInflater; +import android.view.View; + +import androidx.recyclerview.widget.RecyclerView; + +import com.android.launcher3.R; +import com.android.launcher3.allapps.ActivityAllAppsContainerView; +import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem; +import com.android.launcher3.views.ActivityContext; + +import java.util.List; + +/** Initializes the search box and its interactions with All Apps. */ +public class AllAppsSearchUiDelegate { + + protected final ActivityAllAppsContainerView<?> mAppsView; + protected final ActivityContext mActivityContext; + + public AllAppsSearchUiDelegate(ActivityAllAppsContainerView<?> appsView) { + mAppsView = appsView; + mActivityContext = ActivityContext.lookupContext(mAppsView.getContext()); + } + + /** Invoked when an All Apps {@link RecyclerView} is initialized. */ + public void onInitializeRecyclerView(RecyclerView rv) { + // Do nothing. + } + + /** Invoked when search results are updated in All Apps. */ + public void onSearchResultsChanged(List<AdapterItem> results, int searchResultCode) { + // Do nothing. + } + + /** Invoked when transition animations to go to search is completed . */ + public void onAnimateToSearchStateCompleted() { + // Do nothing + } + + /** Invoked when the search bar has been added to All Apps. */ + public void onInitializeSearchBar() { + // Do nothing. + } + + /** Invoked when the search bar has been removed from All Apps. */ + public void onDestroySearchBar() { + // Do nothing. + } + + /** The layout inflater for All Apps and search UI. */ + public LayoutInflater getLayoutInflater() { + return LayoutInflater.from(mAppsView.getContext()); + } + + /** Inflate the search bar for All Apps. */ + public View inflateSearchBar() { + return getLayoutInflater().inflate(R.layout.search_container_all_apps, mAppsView, false); + } + + /** Whether the search box is floating above the apps surface (inset by the IME). */ + public boolean isSearchBarFloating() { + return false; + } + + /** Creates the adapter provider for the main section. */ + public SearchAdapterProvider<?> createMainAdapterProvider() { + return new DefaultSearchAdapterProvider(mActivityContext); + } +} diff --git a/src/com/android/launcher3/anim/AlphaUpdateListener.java b/src/com/android/launcher3/anim/AlphaUpdateListener.java index 8dad1b4fa5..4382174530 100644 --- a/src/com/android/launcher3/anim/AlphaUpdateListener.java +++ b/src/com/android/launcher3/anim/AlphaUpdateListener.java @@ -53,8 +53,18 @@ public class AlphaUpdateListener extends AnimatorListenerAdapter } public static void updateVisibility(View view) { - if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != View.INVISIBLE) { - view.setVisibility(View.INVISIBLE); + updateVisibility(view, View.INVISIBLE); + } + + /** + * Update view's visibility. + * + * @param view View that needs to update visibility. + * @param hiddenVisibility {@link View#GONE} or {@link View#INVISIBLE} + */ + public static void updateVisibility(View view, int hiddenVisibility) { + if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != hiddenVisibility) { + view.setVisibility(hiddenVisibility); } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != View.VISIBLE) { if (view instanceof ViewGroup) { diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java index 1cc0c21745..d11a51f585 100644 --- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java +++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java @@ -15,10 +15,10 @@ */ package com.android.launcher3.anim; +import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.clampToProgress; +import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.Utilities.boundToRange; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.clampToProgress; -import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs; import android.animation.Animator; diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java deleted file mode 100644 index e88654379a..0000000000 --- a/src/com/android/launcher3/anim/Interpolators.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2017 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.launcher3.anim; - -import android.graphics.Path; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; -import android.view.animation.LinearInterpolator; -import android.view.animation.OvershootInterpolator; -import android.view.animation.PathInterpolator; - -import com.android.launcher3.Utilities; - -/** - * Common interpolators used in Launcher - */ -public class Interpolators { - - public static final Interpolator LINEAR = new LinearInterpolator(); - - public static final Interpolator ACCEL = new AccelerateInterpolator(); - public static final Interpolator ACCEL_0_5 = new AccelerateInterpolator(0.5f); - public static final Interpolator ACCEL_0_75 = new AccelerateInterpolator(0.75f); - public static final Interpolator ACCEL_1_5 = new AccelerateInterpolator(1.5f); - public static final Interpolator ACCEL_2 = new AccelerateInterpolator(2); - - public static final Interpolator DEACCEL = new DecelerateInterpolator(); - public static final Interpolator DEACCEL_1_5 = new DecelerateInterpolator(1.5f); - public static final Interpolator DEACCEL_1_7 = new DecelerateInterpolator(1.7f); - public static final Interpolator DEACCEL_2 = new DecelerateInterpolator(2); - public static final Interpolator DEACCEL_2_5 = new DecelerateInterpolator(2.5f); - public static final Interpolator DEACCEL_3 = new DecelerateInterpolator(3f); - - public static final Interpolator ACCEL_DEACCEL = new AccelerateDecelerateInterpolator(); - - public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f); - - public static final Interpolator AGGRESSIVE_EASE = new PathInterpolator(0.2f, 0f, 0f, 1f); - public static final Interpolator AGGRESSIVE_EASE_IN_OUT = new PathInterpolator(0.6f,0, 0.4f, 1); - - public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f); - public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f); - public static final Interpolator PREDICTIVE_BACK_DECELERATED_EASE = - new PathInterpolator(0, 0, 0, 1f); - - /** - * The default emphasized interpolator. Used for hero / emphasized movement of content. - */ - public static final Interpolator EMPHASIZED = createEmphasizedInterpolator(); - public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator( - 0.3f, 0f, 0.8f, 0.15f); - public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator( - 0.05f, 0.7f, 0.1f, 1f); - - public static final Interpolator EXAGGERATED_EASE; - - public static final Interpolator INSTANT = t -> 1; - /** - * All values of t map to 0 until t == 1. This is primarily useful for setting view visibility, - * which should only happen at the very end of the animation (when it's already hidden). - */ - public static final Interpolator FINAL_FRAME = t -> t < 1 ? 0 : 1; - - static { - Path exaggeratedEase = new Path(); - exaggeratedEase.moveTo(0, 0); - exaggeratedEase.cubicTo(0.05f, 0f, 0.133333f, 0.08f, 0.166666f, 0.4f); - exaggeratedEase.cubicTo(0.225f, 0.94f, 0.5f, 1f, 1f, 1f); - EXAGGERATED_EASE = new PathInterpolator(exaggeratedEase); - } - - public static final Interpolator OVERSHOOT_0_75 = new OvershootInterpolator(0.75f); - public static final Interpolator OVERSHOOT_1_2 = new OvershootInterpolator(1.2f); - public static final Interpolator OVERSHOOT_1_7 = new OvershootInterpolator(1.7f); - - public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR = - new PathInterpolator(0.3f, 0f, 0.1f, 1f); - public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL = - v -> ACCEL_DEACCEL.getInterpolation(TOUCH_RESPONSE_INTERPOLATOR.getInterpolation(v)); - - /** - * Inversion of ZOOM_OUT, compounded with an ease-out. - */ - public static final Interpolator ZOOM_IN = new Interpolator() { - @Override - public float getInterpolation(float v) { - return DEACCEL_3.getInterpolation(1 - ZOOM_OUT.getInterpolation(1 - v)); - } - }; - - public static final Interpolator ZOOM_OUT = new Interpolator() { - - private static final float FOCAL_LENGTH = 0.35f; - - @Override - public float getInterpolation(float v) { - return zInterpolate(v); - } - - /** - * This interpolator emulates the rate at which the perceived scale of an object changes - * as its distance from a camera increases. When this interpolator is applied to a scale - * animation on a view, it evokes the sense that the object is shrinking due to moving away - * from the camera. - */ - private float zInterpolate(float input) { - return (1.0f - FOCAL_LENGTH / (FOCAL_LENGTH + input)) / - (1.0f - FOCAL_LENGTH / (FOCAL_LENGTH + 1.0f)); - } - }; - - public static final Interpolator SCROLL = new Interpolator() { - @Override - public float getInterpolation(float t) { - t -= 1.0f; - return t*t*t*t*t + 1; - } - }; - - public static final Interpolator SCROLL_CUBIC = new Interpolator() { - @Override - public float getInterpolation(float t) { - t -= 1.0f; - return t*t*t + 1; - } - }; - - private static final float FAST_FLING_PX_MS = 10; - - public static Interpolator scrollInterpolatorForVelocity(float velocity) { - return Math.abs(velocity) > FAST_FLING_PX_MS ? SCROLL : SCROLL_CUBIC; - } - - /** - * Create an OvershootInterpolator with tension directly related to the velocity (in px/ms). - * @param velocity The start velocity of the animation we want to overshoot. - */ - public static Interpolator overshootInterpolatorForVelocity(float velocity) { - return new OvershootInterpolator(Math.min(Math.abs(velocity), 3f)); - } - - /** - * Returns a function that runs the given interpolator such that the entire progress is set - * between the given bounds. That is, we set the interpolation to 0 until lowerBound and reach - * 1 by upperBound. - */ - public static Interpolator clampToProgress(Interpolator interpolator, float lowerBound, - float upperBound) { - if (upperBound < lowerBound) { - throw new IllegalArgumentException( - String.format("upperBound (%f) must be greater than lowerBound (%f)", - upperBound, lowerBound)); - } - return t -> clampToProgress(interpolator, t, lowerBound, upperBound); - } - - /** - * Returns the progress value's progress between the lower and upper bounds. That is, the - * progress will be 0f from 0f to lowerBound, and reach 1f by upperBound. - * - * Between lowerBound and upperBound, the progress value will be interpolated using the provided - * interpolator. - */ - public static float clampToProgress( - Interpolator interpolator, float progress, float lowerBound, float upperBound) { - if (upperBound < lowerBound) { - throw new IllegalArgumentException( - String.format("upperBound (%f) must be greater than lowerBound (%f)", - upperBound, lowerBound)); - } - - if (progress == lowerBound && progress == upperBound) { - return progress == 0f ? 0 : 1; - } - if (progress < lowerBound) { - return 0; - } - if (progress > upperBound) { - return 1; - } - return interpolator.getInterpolation((progress - lowerBound) / (upperBound - lowerBound)); - } - - /** - * Returns the progress value's progress between the lower and upper bounds. That is, the - * progress will be 0f from 0f to lowerBound, and reach 1f by upperBound. - */ - public static float clampToProgress(float progress, float lowerBound, float upperBound) { - return clampToProgress(Interpolators.LINEAR, progress, lowerBound, upperBound); - } - - /** - * Runs the given interpolator such that the interpolated value is mapped to the given range. - * This is useful, for example, if we only use this interpolator for part of the animation, - * such as to take over a user-controlled animation when they let go. - */ - public static Interpolator mapToProgress(Interpolator interpolator, float lowerBound, - float upperBound) { - return t -> Utilities.mapRange(interpolator.getInterpolation(t), lowerBound, upperBound); - } - - /** - * Returns the reverse of the provided interpolator, following the formula: g(x) = 1 - f(1 - x). - * In practice, this means that if f is an interpolator used to model a value animating between - * m and n, g is the interpolator to use to obtain the specular behavior when animating from n - * to m. - */ - public static Interpolator reverse(Interpolator interpolator) { - return t -> 1 - interpolator.getInterpolation(1 - t); - } - - // Create the default emphasized interpolator - private static PathInterpolator createEmphasizedInterpolator() { - Path path = new Path(); - // Doing the same as fast_out_extra_slow_in - path.moveTo(0f, 0f); - path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f); - path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f); - return new PathInterpolator(path); - } -} diff --git a/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java b/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java index b911928e36..39386fa2ac 100644 --- a/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java +++ b/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java @@ -44,14 +44,30 @@ public class KeyboardInsetAnimationCallback extends WindowInsetsAnimation.Callba private float mInitialTranslation; private float mTerminalTranslation; + private KeyboardTranslationState mKeyboardTranslationState = KeyboardTranslationState.SYSTEM; + + /** Current state of the keyboard. */ + public enum KeyboardTranslationState { + // We are not controlling the keyboard, and it may or may not be translating. + SYSTEM, + // We are about to gain control of the keyboard, but the current state may be transient. + MANUAL_PREPARED, + // We are manually translating the keyboard. + MANUAL_ONGOING + } public KeyboardInsetAnimationCallback(View view) { super(DISPATCH_MODE_STOP); mView = view; } + public KeyboardTranslationState getKeyboardTranslationState() { + return mKeyboardTranslationState; + } + @Override public void onPrepare(WindowInsetsAnimation animation) { + mKeyboardTranslationState = KeyboardTranslationState.MANUAL_PREPARED; mInitialTranslation = mView.getTranslationY(); } @@ -62,6 +78,7 @@ public class KeyboardInsetAnimationCallback extends WindowInsetsAnimation.Callba mTerminalTranslation = mView.getTranslationY(); // Reset the translation in case the view is drawn before onProgress gets called. mView.setTranslationY(mInitialTranslation); + mKeyboardTranslationState = KeyboardTranslationState.MANUAL_ONGOING; if (mView instanceof KeyboardInsetListener) { ((KeyboardInsetListener) mView).onTranslationStart(); } @@ -90,6 +107,10 @@ public class KeyboardInsetAnimationCallback extends WindowInsetsAnimation.Callba mView.setTranslationY(translationY); } + if (mView instanceof KeyboardInsetListener) { + ((KeyboardInsetListener) mView).onKeyboardAlphaChanged(animation.getAlpha()); + } + return windowInsets; } @@ -98,7 +119,7 @@ public class KeyboardInsetAnimationCallback extends WindowInsetsAnimation.Callba if (mView instanceof KeyboardInsetListener) { ((KeyboardInsetListener) mView).onTranslationEnd(); } - super.onEnd(animation); + mKeyboardTranslationState = KeyboardTranslationState.SYSTEM; } /** @@ -111,6 +132,13 @@ public class KeyboardInsetAnimationCallback extends WindowInsetsAnimation.Callba void onTranslationStart(); /** + * Called from {@link KeyboardInsetAnimationCallback#onProgress} + * + * @param alpha the current IME alpha + */ + default void onKeyboardAlphaChanged(float alpha) {} + + /** * Called from {@link KeyboardInsetAnimationCallback#onEnd} */ void onTranslationEnd(); diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java index 40fa0cfd02..bc7b7f00d2 100644 --- a/src/com/android/launcher3/anim/SpringAnimationBuilder.java +++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java @@ -15,7 +15,7 @@ */ package com.android.launcher3.anim; -import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.LINEAR; import android.animation.Animator; import android.animation.ValueAnimator; diff --git a/src/com/android/launcher3/apppairs/AppPairIcon.java b/src/com/android/launcher3/apppairs/AppPairIcon.java new file mode 100644 index 0000000000..1dc4ad293a --- /dev/null +++ b/src/com/android/launcher3/apppairs/AppPairIcon.java @@ -0,0 +1,102 @@ +/* + * 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.launcher3.apppairs; + +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.annotation.Nullable; + +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.R; +import com.android.launcher3.dragndrop.DraggableView; +import com.android.launcher3.model.data.FolderInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.views.ActivityContext; + +import java.util.Collections; +import java.util.Comparator; + +/** + * A {@link android.widget.FrameLayout} used to represent an app pair icon on the workspace. + */ +public class AppPairIcon extends FrameLayout implements DraggableView { + + private ActivityContext mActivity; + private BubbleTextView mAppPairName; + private FolderInfo mInfo; + + public AppPairIcon(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public AppPairIcon(Context context) { + super(context); + } + + /** + * Builds an AppPairIcon to be added to the Launcher + */ + public static AppPairIcon inflateIcon(int resId, ActivityContext activity, + @Nullable ViewGroup group, FolderInfo appPairInfo) { + + LayoutInflater inflater = (group != null) + ? LayoutInflater.from(group.getContext()) + : activity.getLayoutInflater(); + AppPairIcon icon = (AppPairIcon) inflater.inflate(resId, group, false); + + // Sort contents, so that left-hand app comes first + Collections.sort(appPairInfo.contents, Comparator.comparingInt(a -> a.rank)); + + icon.setClipToPadding(false); + icon.mAppPairName = icon.findViewById(R.id.app_pair_icon_name); + + // TODO (jeremysim b/274189428): Replace this placeholder icon + WorkspaceItemInfo placeholder = new WorkspaceItemInfo(); + placeholder.newIcon(icon.getContext()); + icon.mAppPairName.applyFromWorkspaceItem(placeholder); + + icon.mAppPairName.setText(appPairInfo.title); + + icon.setTag(appPairInfo); + icon.setOnClickListener(activity.getItemOnClickListener()); + icon.mInfo = appPairInfo; + icon.mActivity = activity; + + icon.setAccessibilityDelegate(activity.getAccessibilityDelegate()); + + return icon; + } + + @Override + public int getViewType() { + return DRAGGABLE_ICON; + } + + @Override + public void getWorkspaceVisualDragBounds(Rect bounds) { + mAppPairName.getIconBounds(bounds); + } + + public FolderInfo getInfo() { + return mInfo; + } +} diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 76574b69c7..77bd898c97 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -82,15 +82,12 @@ public final class FeatureFlags { * <p> */ // TODO(Block 1): Clean up flags - public static final BooleanFlag ENABLE_ONE_SEARCH_MOTION = getReleaseFlag(270394223, - "ENABLE_ONE_SEARCH_MOTION", ENABLED, "Enables animations in OneSearch."); - public static final BooleanFlag ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES = getReleaseFlag( - 270394041, "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", DISABLED, + 270394041, "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", ENABLED, "Enable option to replace decorator-based search result backgrounds with drawables"); public static final BooleanFlag ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION = getReleaseFlag( - 270394392, "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", DISABLED, + 270394392, "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", ENABLED, "Enable option to launch search results using the new view container transitions"); // TODO(Block 2): Clean up flags @@ -119,7 +116,8 @@ public final class FeatureFlags { // TODO(Block 4): Cleanup flags public static final BooleanFlag ENABLE_FLOATING_SEARCH_BAR = getReleaseFlag(268388460, "ENABLE_FLOATING_SEARCH_BAR", DISABLED, - "Keep All Apps search bar at the bottom (but above keyboard if open)"); + "Allow search bar to persist and animate across states, and attach to" + + " the keyboard from the bottom of the screen"); public static final BooleanFlag ENABLE_ALL_APPS_FROM_OVERVIEW = getDebugFlag(275132633, "ENABLE_ALL_APPS_FROM_OVERVIEW", DISABLED, @@ -131,7 +129,7 @@ public final class FeatureFlags { // TODO(Block 5): Clean up flags public static final BooleanFlag ENABLE_TWOLINE_DEVICESEARCH = getDebugFlag(201388851, - "ENABLE_TWOLINE_DEVICESEARCH", TEAMFOOD, + "ENABLE_TWOLINE_DEVICESEARCH", ENABLED, "Enable two line label for icons with labels on device search."); public static final BooleanFlag ENABLE_ICON_IN_TEXT_HEADER = getDebugFlag(270395143, @@ -185,17 +183,13 @@ public final class FeatureFlags { "Enables predictive back animation from all apps and widgets to home"); // TODO(Block 11): Clean up flags - public static final BooleanFlag ENABLE_TWO_PANEL_HOME = getDebugFlag(270392643, - "ENABLE_TWO_PANEL_HOME", ENABLED, - "Uses two panel on home screen. Only applicable on large screen devices."); - - public static final BooleanFlag FOLDABLE_WORKSPACE_REORDER = getDebugFlag(270395070, - "FOLDABLE_WORKSPACE_REORDER", DISABLED, - "In foldables, when reordering the icons and widgets, is now going to use both sides"); - public static final BooleanFlag FOLDABLE_SINGLE_PAGE = getDebugFlag(270395274, "FOLDABLE_SINGLE_PAGE", DISABLED, "Use a single page for the workspace"); + public static final BooleanFlag ENABLE_PARAMETRIZE_REORDER = getDebugFlag(289420844, + "ENABLE_PARAMETRIZE_REORDER", DISABLED, + "Enables generating the reorder using a set of parameters"); + // TODO(Block 12): Clean up flags public static final BooleanFlag ENABLE_MULTI_INSTANCE = getDebugFlag(270396680, "ENABLE_MULTI_INSTANCE", DISABLED, @@ -236,7 +230,7 @@ public final class FeatureFlags { "COLLECT_SEARCH_HISTORY", DISABLED, "Allow launcher to collect search history for log"); public static final BooleanFlag ENABLE_TWOLINE_ALLAPPS = getDebugFlag(270390937, - "ENABLE_TWOLINE_ALLAPPS", TEAMFOOD, "Enables two line label inside all apps."); + "ENABLE_TWOLINE_ALLAPPS", DISABLED, "Enables two line label inside all apps."); public static final BooleanFlag IME_STICKY_SNACKBAR_EDU = getDebugFlag(270391693, "IME_STICKY_SNACKBAR_EDU", ENABLED, "Show sticky IME edu in AllApps"); @@ -253,6 +247,10 @@ public final class FeatureFlags { "INJECT_FALLBACK_APP_CORPUS_RESULTS", DISABLED, "Inject fallback app corpus result when AiAi fails to return it."); + public static final BooleanFlag ENABLE_LONG_PRESS_NAV_HANDLE = + getReleaseFlag(282993230, "ENABLE_LONG_PRESS_NAV_HANDLE", TEAMFOOD, + "Enables long pressing on the bottom bar nav handle to trigger events."); + // TODO(Block 17): Clean up flags public static final BooleanFlag ENABLE_TASKBAR_PINNING = getDebugFlag(270396583, "ENABLE_TASKBAR_PINNING", DISABLED, @@ -260,11 +258,6 @@ public final class FeatureFlags { + "taskbar flavors"); // TODO(Block 18): Clean up flags - public static final BooleanFlag ENABLE_LAUNCH_FROM_STAGED_APP = getDebugFlag(270395567, - "ENABLE_LAUNCH_FROM_STAGED_APP", ENABLED, - "Enable the ability to tap a staged app during split select to launch it in full " - + "screen"); - public static final BooleanFlag ENABLE_APP_PAIRS = getDebugFlag(274189428, "ENABLE_APP_PAIRS", DISABLED, "Enables the ability to create and save app pairs on the Home screen for easy" @@ -277,7 +270,7 @@ public final class FeatureFlags { + " is enabled or in prefix state"); public static final BooleanFlag ENABLE_SEARCH_UNINSTALLED_APPS = getReleaseFlag(270395269, - "ENABLE_SEARCH_UNINSTALLED_APPS", DISABLED, "Search uninstalled app results."); + "ENABLE_SEARCH_UNINSTALLED_APPS", ENABLED, "Search uninstalled app results."); // TODO(Block 20): Clean up flags public static final BooleanFlag ENABLE_SCRIM_FOR_APP_LAUNCH = getDebugFlag(270393276, @@ -287,16 +280,15 @@ public final class FeatureFlags { "ENABLE_BACK_SWIPE_HOME_ANIMATION", ENABLED, "Enables home animation to icon when user swipes back."); + public static final BooleanFlag ENABLE_DYNAMIC_TASKBAR_THRESHOLDS = getDebugFlag(294252473, + "ENABLE_DYNAMIC_TASKBAR_THRESHOLDS", ENABLED, + "Enables taskbar thresholds that scale based on screen size."); + // TODO(Block 21): Clean up flags public static final BooleanFlag ENABLE_APP_ICON_FOR_INLINE_SHORTCUTS = getDebugFlag(270395087, "ENABLE_APP_ICON_IN_INLINE_SHORTCUTS", DISABLED, "Show app icon for inline shortcut"); // TODO(Block 22): Clean up flags - public static final BooleanFlag RECEIVE_UNFOLD_EVENTS_FROM_SYSUI = getDebugFlag(270397209, - "RECEIVE_UNFOLD_EVENTS_FROM_SYSUI", ENABLED, - "Enables receiving unfold animation events from sysui instead of calculating " - + "them in launcher process using hinge sensor values."); - public static final BooleanFlag ENABLE_WIDGET_TRANSITION_FOR_RESIZING = getDebugFlag(268553314, "ENABLE_WIDGET_TRANSITION_FOR_RESIZING", DISABLED, "Enable widget transition animation when resizing the widgets"); @@ -309,11 +301,11 @@ public final class FeatureFlags { // TODO(Block 23): Clean up flags public static final BooleanFlag ENABLE_GRID_ONLY_OVERVIEW = getDebugFlag(270397206, - "ENABLE_GRID_ONLY_OVERVIEW", DISABLED, + "ENABLE_GRID_ONLY_OVERVIEW", TEAMFOOD, "Enable a grid-only overview without a focused task."); public static final BooleanFlag ENABLE_CURSOR_HOVER_STATES = getDebugFlag(243191650, - "ENABLE_CURSOR_HOVER_STATES", DISABLED, + "ENABLE_CURSOR_HOVER_STATES", TEAMFOOD, "Enables cursor hover states for certain elements."); // TODO(Block 24): Clean up flags @@ -368,10 +360,6 @@ public final class FeatureFlags { "Use inbuilt monochrome icons if app doesn't provide one"); // TODO(Block 28): Clean up flags - public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE = getDebugFlag(270393906, - "ENABLE_SPLIT_FROM_WORKSPACE", ENABLED, - "Enable initiating split screen from workspace."); - public static final BooleanFlag ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS = getDebugFlag(270394122, "ENABLE_SPLIT_FROM_FULLSCREEN_SHORTCUT", DISABLED, "Enable splitting from fullscreen app with keyboard shortcuts"); @@ -380,8 +368,12 @@ public final class FeatureFlags { 270393453, "ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE", DISABLED, "Enable initiating split screen from workspace to workspace."); + public static final BooleanFlag ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE = getDebugFlag( + 279586624, "ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE", DISABLED, + "Enable initiating split screen from desktop mode to workspace."); + public static final BooleanFlag ENABLE_TRACKPAD_GESTURE = getDebugFlag(271010401, - "ENABLE_TRACKPAD_GESTURE", DISABLED, "Enables trackpad gesture."); + "ENABLE_TRACKPAD_GESTURE", ENABLED, "Enables trackpad gesture."); // TODO(Block 29): Clean up flags public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag(270393897, @@ -400,17 +392,25 @@ public final class FeatureFlags { "USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES", DISABLED, "Use local overrides for search request timeout"); - // TODO(Block 31) - public static final BooleanFlag ENABLE_SPLIT_LAUNCH_DATA_REFACTOR = getDebugFlag(279494325, - "ENABLE_SPLIT_LAUNCH_DATA_REFACTOR", ENABLED, - "Use refactored split launching code path"); - - // TODO(Block 32): Empty block + // TODO(Block 31): Clean up flags + // TODO(Block 32): Clean up flags public static final BooleanFlag ENABLE_RESPONSIVE_WORKSPACE = getDebugFlag(241386436, "ENABLE_RESPONSIVE_WORKSPACE", DISABLED, "Enables new workspace grid calculations method."); + // TODO(Block 33): Clean up flags + public static final BooleanFlag ENABLE_ALL_APPS_RV_PREINFLATION = getDebugFlag(288161355, + "ENABLE_ALL_APPS_RV_PREINFLATION", ENABLED, + "Enables preinflating all apps icons to avoid scrolling jank."); + + // TODO(Block 34): Clean up flags + public static final BooleanFlag ALL_APPS_GONE_VISIBILITY = getDebugFlag(291651514, + "ALL_APPS_GONE_VISIBILITY", ENABLED, + "Set all apps container view's hidden visibility to GONE instead of INVISIBLE."); + + // TODO(Block 35): Empty block + public static class BooleanFlag { private final boolean mCurrentValue; diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java index 00f428502c..213c45861c 100644 --- a/src/com/android/launcher3/dragndrop/AddItemActivity.java +++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java @@ -26,7 +26,6 @@ import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY; import android.annotation.TargetApi; -import android.app.ActivityOptions; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.ClipData; @@ -68,6 +67,7 @@ import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.pm.PinRequestHelper; +import com.android.launcher3.uioverrides.ApiWrapper; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.views.AbstractSlideInView; @@ -259,9 +259,7 @@ public class AddItemActivity extends BaseActivity .setPackage(getPackageName()) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); Launcher.ACTIVITY_TRACKER.registerCallback(listener); - startActivity(homeIntent, - ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out) - .toBundle()); + startActivity(homeIntent, ApiWrapper.createFadeOutAnimOptions(this).toBundle()); logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED); mFinishOnPause = true; return false; diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index 98672685e9..0d51d4826a 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -28,9 +28,9 @@ import android.view.View; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.logging.InstanceId; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 366870b4c2..f18f900593 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -19,11 +19,11 @@ package com.android.launcher3.dragndrop; import static android.animation.ObjectAnimator.ofFloat; +import static com.android.app.animation.Interpolators.DECELERATE_1_5; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.Utilities.mapRange; import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; -import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5; import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; import android.animation.Animator; @@ -42,6 +42,7 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.Interpolator; +import com.android.app.animation.Interpolators; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DropTargetBar; import com.android.launcher3.Launcher; @@ -49,7 +50,6 @@ import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.SpringProperty; import com.android.launcher3.celllayout.CellLayoutLayoutParams; @@ -340,14 +340,14 @@ public class DragLayer extends BaseDragLayer<Launcher> implements LauncherOverla if (duration < 0) { duration = res.getInteger(R.integer.config_dropAnimMaxDuration); if (dist < maxDist) { - duration *= DEACCEL_1_5.getInterpolation(dist / maxDist); + duration *= DECELERATE_1_5.getInterpolation(dist / maxDist); } duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration)); } // Fall back to cubic ease out interpolator for the animation if none is specified TimeInterpolator interpolator = - motionInterpolator == null ? DEACCEL_1_5 : motionInterpolator; + motionInterpolator == null ? DECELERATE_1_5 : motionInterpolator; // Animate the view PendingAnimation anim = new PendingAnimation(duration); @@ -475,7 +475,7 @@ public class DragLayer extends BaseDragLayer<Launcher> implements LauncherOverla @Override public void onOverlayScrollChanged(float progress) { - float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(progress); + float alpha = 1 - Interpolators.DECELERATE_3.getInterpolation(progress); float transX = getMeasuredWidth() * progress; if (mIsRtl) { diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index 0d0717e4f6..adfdc8954c 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -55,9 +55,9 @@ import androidx.dynamicanimation.animation.FloatPropertyCompat; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; +import com.android.app.animation.Interpolators; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.icons.FastBitmapDrawable; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.model.data.ItemInfo; @@ -99,7 +99,9 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram // Whether mAnim has started. Unlike mAnim.isStarted(), this is true even after mAnim ends. private boolean mScaleAnimStarted; - private Runnable mOnAnimEndCallback = null; + private boolean mShiftAnimStarted; + private Runnable mOnScaleAnimEndCallback; + private Runnable mOnShiftAnimEndCallback; private int mLastTouchX; private int mLastTouchY; @@ -186,13 +188,26 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); - if (mOnAnimEndCallback != null) { - mOnAnimEndCallback.run(); + if (mOnScaleAnimEndCallback != null) { + mOnScaleAnimEndCallback.run(); } } }); // Set up the shift animator. mShiftAnim = ValueAnimator.ofFloat(0f, 1f); + mShiftAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mShiftAnimStarted = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (mOnShiftAnimEndCallback != null) { + mOnShiftAnimEndCallback.run(); + } + } + }); setDragRegion(new Rect(0, 0, width, height)); @@ -211,8 +226,14 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram setWillNotDraw(false); } - public void setOnAnimationEndCallback(Runnable callback) { - mOnAnimEndCallback = callback; + /** Callback invoked when the scale animation ends. */ + public void setOnScaleAnimEndCallback(Runnable callback) { + mOnScaleAnimEndCallback = callback; + } + + /** Callback invoked when the shift animation ends. */ + public void setOnShiftAnimEndCallback(Runnable callback) { + mOnShiftAnimEndCallback = callback; } /** @@ -224,11 +245,11 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram // Load the adaptive icon on a background thread and add the view in ui thread. MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(() -> { Object[] outObj = new Object[1]; + boolean[] outIsIconThemed = new boolean[1]; int w = mWidth; int h = mHeight; Drawable dr = Utilities.getFullDrawable(mActivity, info, w, h, - true /* shouldThemeIcon */, outObj); - + true /* shouldThemeIcon */, outObj, outIsIconThemed); if (dr instanceof AdaptiveIconDrawable) { int blurMargin = (int) mActivity.getResources() .getDimension(R.dimen.blur_size_medium_outline) / 2; @@ -237,7 +258,7 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram bounds.inset(blurMargin, blurMargin); // Badge is applied after icon normalization so the bounds for badge should not // be scaled down due to icon normalization. - mBadge = getBadge(mActivity, info, outObj[0]); + mBadge = getBadge(mActivity, info, outObj[0], outIsIconThemed[0]); FastBitmapDrawable.setBadgeBounds(mBadge, bounds); // Do not draw the background in case of folder as its translucent @@ -371,7 +392,7 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram AnimatorSet anim = new AnimatorSet(); anim.play(ObjectAnimator.ofFloat(newContent, VIEW_ALPHA, 0, 1)); anim.play(ObjectAnimator.ofFloat(mContent, VIEW_ALPHA, 0)); - anim.setDuration(duration).setInterpolator(Interpolators.DEACCEL_1_5); + anim.setDuration(duration).setInterpolator(Interpolators.DECELERATE_1_5); anim.start(); } @@ -416,10 +437,16 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram } } + /** {@code true} if the scale animation has finished. */ public boolean isScaleAnimationFinished() { return mScaleAnimStarted && !mScaleAnim.isRunning(); } + /** {@code true} if the shift animation has finished. */ + public boolean isShiftAnimationFinished() { + return mShiftAnimStarted && !mShiftAnim.isRunning(); + } + /** * Move the window containing this view. * diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java index af43ae83e2..48b5646779 100644 --- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java +++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java @@ -31,7 +31,6 @@ import android.view.View; import android.widget.RemoteViews; import com.android.launcher3.DragSource; -import com.android.launcher3.Launcher; import com.android.launcher3.PendingAddItemInfo; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.PendingAddShortcutInfo; @@ -72,15 +71,6 @@ public class PinItemDragListener extends BaseItemDragListener { } @Override - public boolean init(Launcher launcher, boolean alreadyOnHome) { - super.init(launcher, alreadyOnHome); - if (!alreadyOnHome) { - launcher.useFadeOutAnimationForLauncherStart(mCancelSignal); - } - return false; - } - - @Override protected PendingItemDragHelper createDragHelper() { final PendingAddItemInfo item; if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) { diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java index 7bdec1cfde..0f3cad66a2 100644 --- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java +++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java @@ -16,6 +16,8 @@ package com.android.launcher3.dragndrop; +import static android.content.pm.LauncherApps.EXTRA_PIN_ITEM_REQUEST; + import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherState.EDIT_MODE; import static com.android.launcher3.LauncherState.SPRING_LOADED; @@ -25,6 +27,7 @@ import android.annotation.TargetApi; import android.app.Activity; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.LauncherApps; import android.content.pm.LauncherApps.PinItemRequest; import android.content.pm.PackageManager; @@ -41,6 +44,7 @@ import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pm.PinRequestHelper; import com.android.launcher3.pm.ShortcutConfigActivityInfo; +import com.android.launcher3.util.StartActivityParams; import java.util.function.Supplier; @@ -105,7 +109,11 @@ public class PinShortcutRequestActivityInfo extends ShortcutConfigActivityInfo { @Override public boolean startConfigActivity(Activity activity, int requestCode) { - return false; + new StartActivityParams(activity, requestCode).deliverResult( + activity, + Activity.RESULT_OK, + new Intent().putExtra(EXTRA_PIN_ITEM_REQUEST, mRequestSupplier.get())); + return true; } @Override diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 4ae54e69f1..55a539a7b6 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -535,7 +535,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo mFolderName.selectAll(); } } - mFolderName.showKeyboard(); + mFolderName.showKeyboard(true /* shouldFocus */); mFolderName.displayCompletions( Stream.of(mInfo.suggestedFolderNames.getLabels()) .filter(Objects::nonNull) @@ -881,7 +881,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo final ItemInfo item = d.dragInfo; final int itemType = item.itemType; return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || - itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT || itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)); } diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java index dd82ecfb95..9e2e2bf5fc 100644 --- a/src/com/android/launcher3/folder/FolderAnimationManager.java +++ b/src/com/android/launcher3/folder/FolderAnimationManager.java @@ -105,11 +105,11 @@ public class FolderAnimationManager { mDelay = res.getInteger(R.integer.config_folderDelay); mFolderInterpolator = AnimationUtils.loadInterpolator(mContext, - R.interpolator.folder_interpolator); + R.interpolator.standard_interpolator); mLargeFolderPreviewItemOpenInterpolator = AnimationUtils.loadInterpolator(mContext, R.interpolator.large_folder_preview_item_open_interpolator); mLargeFolderPreviewItemCloseInterpolator = AnimationUtils.loadInterpolator(mContext, - R.interpolator.large_folder_preview_item_close_interpolator); + R.interpolator.standard_accelerate_interpolator); } /** @@ -236,9 +236,9 @@ public class FolderAnimationManager { mFolder, startRect, endRect, finalRadius, !mIsOpening)); // Create reveal animator for the folder content (capture the top 4 icons 2x2) - int width = mDeviceProfile.folderCellLayoutBorderSpacePx + int width = mDeviceProfile.folderCellLayoutBorderSpacePx.x + mDeviceProfile.folderCellWidthPx * 2; - int height = mDeviceProfile.folderCellLayoutBorderSpacePx + int height = mDeviceProfile.folderCellLayoutBorderSpacePx.y + mDeviceProfile.folderCellHeightPx * 2; int page = mIsOpening ? mContent.getCurrentPage() : mContent.getDestinationPage(); int left = mContent.getPaddingLeft() + page * lp.width; diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 2c1100f86e..53d0efbe85 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -16,6 +16,7 @@ package com.android.launcher3.folder; +import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES; import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR; import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION; @@ -42,6 +43,7 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.launcher3.Alarm; import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; @@ -56,7 +58,6 @@ import com.android.launcher3.Reorderable; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.allapps.ActivityAllAppsContainerView; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.celllayout.CellLayoutLayoutParams; import com.android.launcher3.dot.FolderDotInfo; import com.android.launcher3.dragndrop.BaseItemDragListener; @@ -260,7 +261,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel private boolean willAcceptItem(ItemInfo item) { final int itemType = item.itemType; return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || - itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT || itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) && item != mInfo && !mFolder.isOpen()); } @@ -407,7 +407,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel final int finalIndex = index; dragLayer.animateView(animateView, to, finalAlpha, finalScale, finalScale, DROP_IN_ANIMATION_DURATION, - Interpolators.DEACCEL_2, + Interpolators.DECELERATE_2, () -> { mPreviewItemManager.hidePreviewItem(finalIndex, false); mFolder.showItem(item); @@ -628,7 +628,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel Utilities.scaleRectAboutCenter(iconBounds, iconScale); // If we are animating to the accepting state, animate the dot out. - mDotParams.scale = Math.max(0, mDotScale - mBackground.getScaleProgress()); + mDotParams.scale = Math.max(0, mDotScale - mBackground.getAcceptScaleProgress()); mDotParams.dotColor = mBackground.getDotColor(); mDotRenderer.draw(canvas, mDotParams); } @@ -802,6 +802,14 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } } + @Override + public void onHoverChanged(boolean hovered) { + super.onHoverChanged(hovered); + if (ENABLE_CURSOR_HOVER_STATES.get()) { + mBackground.setHovered(hovered); + } + } + /** * Interface that provides callbacks to a parent ViewGroup that hosts this FolderIcon. */ diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java index 7ac2472319..c06a0f3c53 100644 --- a/src/com/android/launcher3/folder/LauncherDelegate.java +++ b/src/com/android/launcher3/folder/LauncherDelegate.java @@ -94,9 +94,6 @@ public class LauncherDelegate { // folder CellLayout cellLayout = mLauncher.getCellLayout(info.container, mLauncher.getCellPosMapper().mapModelToPresenter(info).screenId); - if (cellLayout == null) { - return; - } finalItem = info.contents.remove(0); newIcon = mLauncher.createShortcut(cellLayout, finalItem); mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem, diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java index 406955c417..b320cebcfc 100644 --- a/src/com/android/launcher3/folder/PreviewBackground.java +++ b/src/com/android/launcher3/folder/PreviewBackground.java @@ -16,6 +16,8 @@ package com.android.launcher3.folder; +import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE; +import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE; import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR; import static com.android.launcher3.graphics.IconShape.getShape; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; @@ -39,6 +41,9 @@ import android.graphics.Region; import android.graphics.Shader; import android.util.Property; import android.view.View; +import android.view.animation.Interpolator; + +import androidx.annotation.VisibleForTesting; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; @@ -55,7 +60,10 @@ public class PreviewBackground extends CellLayout.DelegatedCellDrawing { private static final boolean DRAW_SHADOW = false; private static final boolean DRAW_STROKE = false; - private static final int CONSUMPTION_ANIMATION_DURATION = 100; + @VisibleForTesting protected static final int CONSUMPTION_ANIMATION_DURATION = 100; + + @VisibleForTesting protected static final float HOVER_SCALE = 1.1f; + @VisibleForTesting protected static final int HOVER_ANIMATION_DURATION = 300; private final PorterDuffXfermode mShadowPorterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT); @@ -86,17 +94,21 @@ public class PreviewBackground extends CellLayout.DelegatedCellDrawing { public boolean isClipping = true; // Drawing / animation configurations - private static final float ACCEPT_SCALE_FACTOR = 1.20f; + @VisibleForTesting protected static final float ACCEPT_SCALE_FACTOR = 1.20f; // Expressed on a scale from 0 to 255. private static final int BG_OPACITY = 255; private static final int MAX_BG_OPACITY = 255; private static final int SHADOW_OPACITY = 40; - private ValueAnimator mScaleAnimator; + @VisibleForTesting protected ValueAnimator mScaleAnimator; private ObjectAnimator mStrokeAlphaAnimator; private ObjectAnimator mShadowAnimator; + @VisibleForTesting protected boolean mIsAccepting; + @VisibleForTesting protected boolean mIsHovered; + @VisibleForTesting protected boolean mIsHoveredOrAnimating; + private static final Property<PreviewBackground, Integer> STROKE_ALPHA = new Property<PreviewBackground, Integer>(Integer.class, "strokeAlpha") { @Override @@ -203,11 +215,11 @@ public class PreviewBackground extends CellLayout.DelegatedCellDrawing { } /** - * Returns the progress of the scale animation, where 0 means the scale is at 1f - * and 1 means the scale is at ACCEPT_SCALE_FACTOR. + * Returns the progress of the scale animation to accept state, where 0 means the scale is at + * 1f and 1 means the scale is at ACCEPT_SCALE_FACTOR. Returns 0 when scaled due to hover. */ - float getScaleProgress() { - return (mScale - 1f) / (ACCEPT_SCALE_FACTOR - 1f); + float getAcceptScaleProgress() { + return mIsHoveredOrAnimating ? 0 : (mScale - 1f) / (ACCEPT_SCALE_FACTOR - 1f); } void invalidate() { @@ -385,60 +397,70 @@ public class PreviewBackground extends CellLayout.DelegatedCellDrawing { return mDrawingDelegate != null; } - private void animateScale(float finalScale, final Runnable onStart, final Runnable onEnd) { - final float scale0 = mScale; - final float scale1 = finalScale; - + protected void animateScale(boolean isAccepting, boolean isHovered) { if (mScaleAnimator != null) { mScaleAnimator.cancel(); } - mScaleAnimator = ValueAnimator.ofFloat(0f, 1.0f); - - mScaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float prog = animation.getAnimatedFraction(); - mScale = prog * scale1 + (1 - prog) * scale0; - invalidate(); + final float startScale = mScale; + final float endScale = isAccepting ? ACCEPT_SCALE_FACTOR : (isHovered ? HOVER_SCALE : 1f); + Interpolator interpolator = + isAccepting != mIsAccepting ? ACCELERATE_DECELERATE : EMPHASIZED_DECELERATE; + int duration = isAccepting != mIsAccepting ? CONSUMPTION_ANIMATION_DURATION + : HOVER_ANIMATION_DURATION; + mIsAccepting = isAccepting; + mIsHovered = isHovered; + if (startScale == endScale) { + if (!mIsAccepting) { + clearDrawingDelegate(); } + mIsHoveredOrAnimating = mIsHovered; + return; + } + + + mScaleAnimator = ValueAnimator.ofFloat(0f, 1.0f); + mScaleAnimator.addUpdateListener(animation -> { + float prog = animation.getAnimatedFraction(); + mScale = prog * endScale + (1 - prog) * startScale; + invalidate(); }); mScaleAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - if (onStart != null) { - onStart.run(); + if (mIsHovered) { + mIsHoveredOrAnimating = true; } } @Override public void onAnimationEnd(Animator animation) { - if (onEnd != null) { - onEnd.run(); + if (!mIsAccepting) { + clearDrawingDelegate(); } + mIsHoveredOrAnimating = mIsHovered; mScaleAnimator = null; } }); - - mScaleAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION); + mScaleAnimator.setInterpolator(interpolator); + mScaleAnimator.setDuration(duration); mScaleAnimator.start(); } public void animateToAccept(CellLayout cl, int cellX, int cellY) { - animateScale(ACCEPT_SCALE_FACTOR, () -> delegateDrawing(cl, cellX, cellY), null); + delegateDrawing(cl, cellX, cellY); + animateScale(/* isAccepting= */ true, mIsHovered); } public void animateToRest() { - // This can be called multiple times -- we need to make sure the drawing delegate - // is saved and restored at the beginning of the animation, since cancelling the - // existing animation can clear the delgate. - CellLayout cl = mDrawingDelegate; - int cellX = mDelegateCellX; - int cellY = mDelegateCellY; - animateScale(1f, () -> delegateDrawing(cl, cellX, cellY), this::clearDrawingDelegate); + animateScale(/* isAccepting= */ false, mIsHovered); } public float getStrokeWidth() { return mStrokeWidth; } + + protected void setHovered(boolean hovered) { + animateScale(mIsAccepting, /* isHovered= */ hovered); + } } diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java index 7457f3057c..42083431e1 100644 --- a/src/com/android/launcher3/graphics/DragPreviewProvider.java +++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java @@ -16,15 +16,19 @@ package com.android.launcher3.graphics; +import static com.android.launcher3.BubbleTextView.DISPLAY_SEARCH_RESULT_APP_ROW; + import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.graphics.drawable.InsetDrawable; import android.view.View; import androidx.annotation.Nullable; +import com.android.launcher3.BubbleTextView; import com.android.launcher3.R; import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.icons.BitmapRenderer; @@ -37,7 +41,6 @@ import com.android.launcher3.widget.LauncherAppWidgetHostView; * A utility class to generate preview bitmap for dragging. */ public class DragPreviewProvider { - private final Rect mTempRect = new Rect(); protected final View mView; @@ -99,6 +102,15 @@ public class DragPreviewProvider { height = mView.getHeight(); } + if (mView instanceof BubbleTextView btv + && btv.getIconDisplay() == DISPLAY_SEARCH_RESULT_APP_ROW) { + FastBitmapDrawable icon = ((BubbleTextView) mView).getIcon(); + Drawable drawable = icon.getConstantState().newDrawable(); + float xInset = (float) blurSizeOutline / (float) (width + blurSizeOutline); + float yInset = (float) blurSizeOutline / (float) (height + blurSizeOutline); + return new InsetDrawable(drawable, xInset / 2, yInset / 2, xInset / 2, yInset / 2); + } + return new FastBitmapDrawable( BitmapRenderer.createHardwareBitmap(width + blurSizeOutline, height + blurSizeOutline, (c) -> drawDragView(c, scale))); diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index 47677eab8c..ae44f0a498 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -34,13 +34,9 @@ import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; import android.content.ContextWrapper; -import android.content.Intent; import android.content.res.TypedArray; -import android.graphics.Color; import android.graphics.PointF; import android.graphics.Rect; -import android.graphics.drawable.AdaptiveIconDrawable; -import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.os.Handler; import android.os.Looper; @@ -49,12 +45,12 @@ import android.util.Size; import android.util.SparseArray; import android.util.SparseIntArray; import android.view.ContextThemeWrapper; +import android.view.Display; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.WindowInsets; -import android.view.WindowManager; +import android.widget.FrameLayout; import android.widget.TextClock; import androidx.annotation.NonNull; @@ -74,12 +70,11 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.WorkspaceLayoutManager; +import com.android.launcher3.apppairs.AppPairIcon; import com.android.launcher3.celllayout.CellLayoutLayoutParams; import com.android.launcher3.celllayout.CellPosMapper; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.FolderIcon; -import com.android.launcher3.icons.BaseIconFactory; -import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.FixedContainerItems; @@ -98,6 +93,7 @@ import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext; +import com.android.launcher3.util.WindowBounds; import com.android.launcher3.util.window.WindowManagerProxy; import com.android.launcher3.views.ActivityContext; import com.android.launcher3.views.BaseDragLayer; @@ -183,7 +179,6 @@ public class LauncherPreviewRenderer extends ContextWrapper private final DeviceProfile mDp; private final DeviceProfile mDpOrig; private final Rect mInsets; - private final WorkspaceItemInfo mWorkspaceItemInfo; private final LayoutInflater mHomeElementInflater; private final InsettableFrameLayout mRootView; private final Hotseat mHotseat; @@ -211,29 +206,9 @@ public class LauncherPreviewRenderer extends ContextWrapper } else { mDpOrig = mDp; } - - WindowInsets currentWindowInsets = context.getSystemService(WindowManager.class) - .getCurrentWindowMetrics().getWindowInsets(); - mInsets = new Rect( - currentWindowInsets.getSystemWindowInsetLeft(), - currentWindowInsets.getSystemWindowInsetTop(), - currentWindowInsets.getSystemWindowInsetRight(), - mDp.isTaskbarPresent ? 0 : currentWindowInsets.getSystemWindowInsetBottom()); + mInsets = getInsets(context); mDp.updateInsets(mInsets); - BaseIconFactory iconFactory = - new BaseIconFactory(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize) { }; - BitmapInfo iconInfo = iconFactory.createBadgedIconBitmap( - new AdaptiveIconDrawable( - new ColorDrawable(Color.WHITE), - new ColorDrawable(Color.WHITE))); - - mWorkspaceItemInfo = new WorkspaceItemInfo(); - mWorkspaceItemInfo.bitmap = iconInfo; - mWorkspaceItemInfo.intent = new Intent(); - mWorkspaceItemInfo.contentDescription = mWorkspaceItemInfo.title = - context.getString(R.string.label_application); - mHomeElementInflater = LayoutInflater.from( new ContextThemeWrapper(this, R.style.HomeScreenElementTheme)); mHomeElementInflater.setFactory2(this); @@ -283,6 +258,26 @@ public class LauncherPreviewRenderer extends ContextWrapper mAppWidgetHost = new LauncherPreviewAppWidgetHost(context); } + /** + * Returns the insets of the screen closest to the display given by the context + */ + private Rect getInsets(Context context) { + DisplayController.Info info = DisplayController.INSTANCE.get(context).getInfo(); + float maxDiff = Float.MAX_VALUE; + Display display = context.getDisplay(); + Rect insets = new Rect(); + for (WindowBounds supportedBound : info.supportedBounds) { + double diff = Math.pow(display.getWidth() - supportedBound.availableSize.x, 2) + + Math.pow(display.getHeight() - supportedBound.availableSize.y, 2); + if (supportedBound.rotationHint == context.getDisplay().getRotation() + && diff < maxDiff) { + maxDiff = (float) diff; + insets = supportedBound.insets; + } + } + return new Rect(insets); + } + /** Populate preview and render it. */ public View getRenderedView(BgDataModel dataModel, Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) { @@ -378,12 +373,13 @@ public class LauncherPreviewRenderer extends ContextWrapper addInScreenFromBind(icon, info); } - private void inflateAndAddFolder(FolderInfo info) { + private void inflateAndAddCollectionIcon(FolderInfo info) { CellLayout screen = info.container == Favorites.CONTAINER_DESKTOP ? mWorkspaceScreens.get(info.screenId) : mHotseat; - FolderIcon folderIcon = FolderIcon.inflateIcon(R.layout.folder_icon, this, screen, - info); + FrameLayout folderIcon = info.itemType == Favorites.ITEM_TYPE_FOLDER + ? FolderIcon.inflateIcon(R.layout.folder_icon, this, screen, info) + : AppPairIcon.inflateIcon(R.layout.app_pair_icon, this, screen, info); addInScreenFromBind(folderIcon, info); } @@ -483,12 +479,12 @@ public class LauncherPreviewRenderer extends ContextWrapper for (ItemInfo itemInfo : currentWorkspaceItems) { switch (itemInfo.itemType) { case Favorites.ITEM_TYPE_APPLICATION: - case Favorites.ITEM_TYPE_SHORTCUT: case Favorites.ITEM_TYPE_DEEP_SHORTCUT: inflateAndAddIcon((WorkspaceItemInfo) itemInfo); break; case Favorites.ITEM_TYPE_FOLDER: - inflateAndAddFolder((FolderInfo) itemInfo); + case Favorites.ITEM_TYPE_APP_PAIR: + inflateAndAddCollectionIcon((FolderInfo) itemInfo); break; default: break; diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java index 2dd92a1d00..307052abdb 100644 --- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java +++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java @@ -17,8 +17,8 @@ package com.android.launcher3.graphics; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED; -import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.EMPHASIZED; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.config.FeatureFlags.ENABLE_DOWNLOAD_APP_UX_V2; import static com.android.launcher3.config.FeatureFlags.ENABLE_DOWNLOAD_APP_UX_V3; diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java index e89c0c5d11..aebcdd4aae 100644 --- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java +++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java @@ -16,6 +16,8 @@ package com.android.launcher3.graphics; +import static android.view.Display.DEFAULT_DISPLAY; + import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; @@ -80,11 +82,12 @@ public class PreviewSurfaceRenderer { private static final String KEY_DISPLAY_ID = "display_id"; private static final String KEY_COLORS = "wallpaper_colors"; - private final Context mContext; - private final InvariantDeviceProfile mIdp; + private Context mContext; private final IBinder mHostToken; private final int mWidth; private final int mHeight; + private String mGridName; + private final Display mDisplay; private final WallpaperColors mWallpaperColors; private final RunnableList mOnDestroyCallbacks = new RunnableList(); @@ -97,15 +100,13 @@ public class PreviewSurfaceRenderer { public PreviewSurfaceRenderer(Context context, Bundle bundle) throws Exception { mContext = context; - - String gridName = bundle.getString("name"); + mGridName = bundle.getString("name"); bundle.remove("name"); - if (gridName == null) { - gridName = InvariantDeviceProfile.getCurrentGridName(context); + if (mGridName == null) { + mGridName = InvariantDeviceProfile.getCurrentGridName(context); } mWallpaperColors = bundle.getParcelable(KEY_COLORS); mHideQsb = bundle.getBoolean(GridCustomizationsProvider.KEY_HIDE_BOTTOM_ROW); - mIdp = new InvariantDeviceProfile(context, gridName); mHostToken = bundle.getBinder(KEY_HOST_TOKEN); mWidth = bundle.getInt(KEY_VIEW_WIDTH); @@ -113,9 +114,9 @@ public class PreviewSurfaceRenderer { mDisplay = context.getSystemService(DisplayManager.class) .getDisplay(bundle.getInt(KEY_DISPLAY_ID)); - mSurfaceControlViewHost = MAIN_EXECUTOR - .submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken)) - .get(5, TimeUnit.SECONDS); + mSurfaceControlViewHost = MAIN_EXECUTOR.submit(() -> new SurfaceControlViewHost(mContext, + context.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY), + mHostToken)).get(5, TimeUnit.SECONDS); mOnDestroyCallbacks.add(mSurfaceControlViewHost::release); } @@ -195,28 +196,33 @@ public class PreviewSurfaceRenderer { } } - @WorkerThread - private void loadModelData() { - final Context inflationContext; - if (mWallpaperColors != null) { - // Create a themed context, without affecting the main application context - Context context = mContext.createDisplayContext(mDisplay); - if (Utilities.ATLEAST_R) { - context = context.createWindowContext( - LayoutParams.TYPE_APPLICATION_OVERLAY, null); - } - LocalColorExtractor.newInstance(mContext) - .applyColorsOverride(context, mWallpaperColors); - inflationContext = new ContextThemeWrapper(context, - Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints())); - } else { - inflationContext = new ContextThemeWrapper(mContext, - Themes.getActivityThemeRes(mContext)); + /*** + * Generates a new context overriding the theme color and the display size without affecting the + * main application context + */ + private Context getPreviewContext() { + Context context = mContext.createDisplayContext(mDisplay); + if (mWallpaperColors == null) { + return new ContextThemeWrapper(context, + Themes.getActivityThemeRes(context)); + } + if (Utilities.ATLEAST_R) { + context = context.createWindowContext( + LayoutParams.TYPE_APPLICATION_OVERLAY, null); } + LocalColorExtractor.newInstance(context) + .applyColorsOverride(context, mWallpaperColors); + return new ContextThemeWrapper(context, + Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints())); + } - if (GridSizeMigrationUtil.needsToMigrate(inflationContext, mIdp)) { + @WorkerThread + private void loadModelData() { + final Context inflationContext = getPreviewContext(); + final InvariantDeviceProfile idp = new InvariantDeviceProfile(inflationContext, mGridName); + if (GridSizeMigrationUtil.needsToMigrate(inflationContext, idp)) { // Start the migration - PreviewContext previewContext = new PreviewContext(inflationContext, mIdp); + PreviewContext previewContext = new PreviewContext(inflationContext, idp); // Copy existing data to preview DB LauncherDbUtils.copyTable(LauncherAppState.getInstance(mContext) .getModel().getModelDbController().getDb(), @@ -239,7 +245,7 @@ public class PreviewSurfaceRenderer { @Override public void run() { - DeviceProfile deviceProfile = mIdp.getDeviceProfile(previewContext); + DeviceProfile deviceProfile = idp.getDeviceProfile(previewContext); String query = LauncherSettings.Favorites.SCREEN + " = " + Workspace.FIRST_SCREEN_ID + " or " + LauncherSettings.Favorites.CONTAINER + " = " @@ -254,7 +260,8 @@ public class PreviewSurfaceRenderer { getLoadedLauncherWidgetInfo(previewContext.getBaseContext()); MAIN_EXECUTOR.execute(() -> { - renderView(previewContext, mBgDataModel, mWidgetProvidersMap, spanInfo); + renderView(previewContext, mBgDataModel, mWidgetProvidersMap, spanInfo, + idp); mOnDestroyCallbacks.add(previewContext::onDestroy); }); } @@ -263,7 +270,7 @@ public class PreviewSurfaceRenderer { LauncherAppState.getInstance(inflationContext).getModel().loadAsync(dataModel -> { if (dataModel != null) { MAIN_EXECUTOR.execute(() -> renderView(inflationContext, dataModel, null, - null)); + null, idp)); } else { Log.e(TAG, "Model loading failed"); } @@ -274,11 +281,11 @@ public class PreviewSurfaceRenderer { @UiThread private void renderView(Context inflationContext, BgDataModel dataModel, Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap, - @Nullable final SparseArray<Size> launcherWidgetSpanInfo) { + @Nullable final SparseArray<Size> launcherWidgetSpanInfo, InvariantDeviceProfile idp) { if (mDestroyed) { return; } - mRenderer = new LauncherPreviewRenderer(inflationContext, mIdp, + mRenderer = new LauncherPreviewRenderer(inflationContext, idp, mWallpaperColors, launcherWidgetSpanInfo); mRenderer.hideBottomRow(mHideQsb); View view = mRenderer.getRenderedView(dataModel, widgetProviderInfoMap); diff --git a/src/com/android/launcher3/graphics/SysUiScrim.java b/src/com/android/launcher3/graphics/SysUiScrim.java index 21ebc989fd..66001d8125 100644 --- a/src/com/android/launcher3/graphics/SysUiScrim.java +++ b/src/com/android/launcher3/graphics/SysUiScrim.java @@ -13,29 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.android.launcher3.graphics; +import static android.graphics.Paint.DITHER_FLAG; +import static android.graphics.Paint.FILTER_BITMAP_FLAG; + import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION; -import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; import android.animation.ObjectAnimator; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; -import android.graphics.drawable.Drawable; import android.util.DisplayMetrics; -import android.util.FloatProperty; import android.view.View; +import androidx.annotation.ColorInt; +import androidx.annotation.VisibleForTesting; + import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; +import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.testing.shared.ResourceUtils; import com.android.launcher3.util.ScreenOnTracker; import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener; @@ -46,33 +48,6 @@ import com.android.launcher3.util.Themes; */ public class SysUiScrim implements View.OnAttachStateChangeListener { - public static final FloatProperty<SysUiScrim> SYSUI_PROGRESS = - new FloatProperty<SysUiScrim>("sysUiProgress") { - @Override - public Float get(SysUiScrim scrim) { - return scrim.mSysUiProgress; - } - - @Override - public void setValue(SysUiScrim scrim, float value) { - scrim.setSysUiProgress(value); - } - }; - - private static final FloatProperty<SysUiScrim> SYSUI_ANIM_MULTIPLIER = - new FloatProperty<SysUiScrim>("sysUiAnimMultiplier") { - @Override - public Float get(SysUiScrim scrim) { - return scrim.mSysUiAnimMultiplier; - } - - @Override - public void setValue(SysUiScrim scrim, float value) { - scrim.mSysUiAnimMultiplier = value; - scrim.reapplySysUiAlpha(); - } - }; - /** * Receiver used to get a signal that the user unlocked their device. */ @@ -92,44 +67,53 @@ public class SysUiScrim implements View.OnAttachStateChangeListener { } }; - private static final int MAX_HOTSEAT_SCRIM_ALPHA = 100; - private static final int ALPHA_MASK_HEIGHT_DP = 500; - private static final int ALPHA_MASK_BITMAP_DP = 200; - private static final int ALPHA_MASK_WIDTH_DP = 2; + private static final int MAX_SYSUI_SCRIM_ALPHA = 255; + private static final int ALPHA_MASK_BITMAP_WIDTH_DP = 2; + + private static final int BOTTOM_MASK_HEIGHT_DP = 200; + private static final int TOP_MASK_HEIGHT_DP = 100; private boolean mDrawTopScrim, mDrawBottomScrim; - private final RectF mFinalMaskRect = new RectF(); - private final Paint mBottomMaskPaint = new Paint(Paint.FILTER_BITMAP_FLAG); - private final Bitmap mBottomMask; - private final int mMaskHeight; + private final RectF mTopMaskRect = new RectF(); + private final Paint mTopMaskPaint = new Paint(FILTER_BITMAP_FLAG | DITHER_FLAG); + private final Bitmap mTopMaskBitmap; + private final int mTopMaskHeight; + + private final RectF mBottomMaskRect = new RectF(); + private final Paint mBottomMaskPaint = new Paint(FILTER_BITMAP_FLAG | DITHER_FLAG); + private final Bitmap mBottomMaskBitmap; + private final int mBottomMaskHeight; private final View mRoot; private final BaseDraggingActivity mActivity; - private final Drawable mTopScrim; - - private float mSysUiProgress = 1; - private boolean mHideSysUiScrim; + private final boolean mHideSysUiScrim; + private boolean mSkipScrimAnimationForTest = false; private boolean mAnimateScrimOnNextDraw = false; - private float mSysUiAnimMultiplier = 1; + private final AnimatedFloat mSysUiAnimMultiplier = new AnimatedFloat(this::reapplySysUiAlpha); + private final AnimatedFloat mSysUiProgress = new AnimatedFloat(this::reapplySysUiAlpha); public SysUiScrim(View view) { mRoot = view; mActivity = BaseDraggingActivity.fromContext(view.getContext()); - mMaskHeight = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_DP, - view.getResources().getDisplayMetrics()); - mTopScrim = Themes.getAttrDrawable(view.getContext(), R.attr.workspaceStatusBarScrim); - if (mTopScrim != null) { - mTopScrim.setDither(true); - mBottomMask = createDitheredAlphaMask(); - mHideSysUiScrim = false; - } else { - mBottomMask = null; - mHideSysUiScrim = true; - } + DisplayMetrics dm = mActivity.getResources().getDisplayMetrics(); - view.addOnAttachStateChangeListener(this); + mTopMaskHeight = ResourceUtils.pxFromDp(TOP_MASK_HEIGHT_DP, dm); + mBottomMaskHeight = ResourceUtils.pxFromDp(BOTTOM_MASK_HEIGHT_DP, dm); + mHideSysUiScrim = Themes.getAttrBoolean(view.getContext(), R.attr.isWorkspaceDarkText); + + mTopMaskBitmap = mHideSysUiScrim ? null : createDitheredAlphaMask(mTopMaskHeight, + new int[]{0x50FFFFFF, 0x0AFFFFFF, 0x00FFFFFF}, + new float[]{0f, 0.7f, 1f}); + mTopMaskPaint.setColor(0xFF222222); + mBottomMaskBitmap = mHideSysUiScrim ? null : createDitheredAlphaMask(mBottomMaskHeight, + new int[]{0x00FFFFFF, 0x2FFFFFFF}, + new float[]{0f, 1f}); + + if (!KEYGUARD_ANIMATION.get() && !mHideSysUiScrim) { + view.addOnAttachStateChangeListener(this); + } } /** @@ -137,16 +121,16 @@ public class SysUiScrim implements View.OnAttachStateChangeListener { */ public void draw(Canvas canvas) { if (!mHideSysUiScrim) { - if (mSysUiProgress <= 0) { + if (mSysUiProgress.value <= 0) { mAnimateScrimOnNextDraw = false; return; } if (mAnimateScrimOnNextDraw) { - mSysUiAnimMultiplier = 0; + mSysUiAnimMultiplier.value = 0; reapplySysUiAlphaNoInvalidate(); - ObjectAnimator oa = createSysuiMultiplierAnim(1); + ObjectAnimator oa = mSysUiAnimMultiplier.animateToValue(1); oa.setDuration(600); oa.setStartDelay(mActivity.getWindow().getTransitionBackgroundFadeDuration()); oa.start(); @@ -154,21 +138,26 @@ public class SysUiScrim implements View.OnAttachStateChangeListener { } if (mDrawTopScrim) { - mTopScrim.draw(canvas); + canvas.drawBitmap(mTopMaskBitmap, null, mTopMaskRect, mTopMaskPaint); } if (mDrawBottomScrim) { - canvas.drawBitmap(mBottomMask, null, mFinalMaskRect, mBottomMaskPaint); + canvas.drawBitmap(mBottomMaskBitmap, null, mBottomMaskRect, mBottomMaskPaint); } } } /** - * @return an ObjectAnimator that controls the fade in/out of the sys ui scrim. + * Returns the sysui multiplier property for controlling fade in/out of the scrim */ - public ObjectAnimator createSysuiMultiplierAnim(float... values) { - ObjectAnimator anim = ObjectAnimator.ofFloat(this, SYSUI_ANIM_MULTIPLIER, values); - anim.setAutoCancel(true); - return anim; + public AnimatedFloat getSysUIMultiplier() { + return mSysUiAnimMultiplier; + } + + /** + * Returns the sysui progress property for controlling fade in/out of the scrim + */ + public AnimatedFloat getSysUIProgress() { + return mSysUiProgress; } /** @@ -180,44 +169,35 @@ public class SysUiScrim implements View.OnAttachStateChangeListener { */ public void onInsetsChanged(Rect insets) { DeviceProfile dp = mActivity.getDeviceProfile(); - mDrawTopScrim = mTopScrim != null && insets.top > 0; - mDrawBottomScrim = mBottomMask != null - && !dp.isVerticalBarLayout() - && !dp.isGestureMode - && !dp.isTaskbarPresent; + mDrawTopScrim = insets.top > 0; + mDrawBottomScrim = !dp.isVerticalBarLayout() && !dp.isGestureMode && !dp.isTaskbarPresent; } @Override public void onViewAttachedToWindow(View view) { - if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) { - ScreenOnTracker.INSTANCE.get(mActivity).addListener(mScreenOnListener); - } + ScreenOnTracker.INSTANCE.get(mActivity).addListener(mScreenOnListener); } @Override public void onViewDetachedFromWindow(View view) { - if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) { - ScreenOnTracker.INSTANCE.get(mActivity).removeListener(mScreenOnListener); - } + ScreenOnTracker.INSTANCE.get(mActivity).removeListener(mScreenOnListener); } /** * Set the width and height of the view being scrimmed - * @param w - * @param h */ public void setSize(int w, int h) { - if (mTopScrim != null) { - mTopScrim.setBounds(0, 0, w, h); - mFinalMaskRect.set(0, h - mMaskHeight, w, h); - } + mTopMaskRect.set(0, 0, w, mTopMaskHeight); + mBottomMaskRect.set(0, h - mBottomMaskHeight, w, h); } - private void setSysUiProgress(float progress) { - if (progress != mSysUiProgress) { - mSysUiProgress = progress; - reapplySysUiAlpha(); - } + /** + * Sets whether the SysUiScrim should hide for testing. + */ + @VisibleForTesting + public void skipScrimAnimation() { + mSkipScrimAnimationForTest = true; + reapplySysUiAlpha(); } private void reapplySysUiAlpha() { @@ -228,29 +208,22 @@ public class SysUiScrim implements View.OnAttachStateChangeListener { } private void reapplySysUiAlphaNoInvalidate() { - float factor = mSysUiProgress * mSysUiAnimMultiplier; - mBottomMaskPaint.setAlpha(Math.round(MAX_HOTSEAT_SCRIM_ALPHA * factor)); - if (mTopScrim != null) { - mTopScrim.setAlpha(Math.round(255 * factor)); - } + float factor = mSysUiProgress.value * mSysUiAnimMultiplier.value; + if (mSkipScrimAnimationForTest) factor = 1f; + mBottomMaskPaint.setAlpha(Math.round(MAX_SYSUI_SCRIM_ALPHA * factor)); + mTopMaskPaint.setAlpha(Math.round(MAX_SYSUI_SCRIM_ALPHA * factor)); } - private Bitmap createDitheredAlphaMask() { + private Bitmap createDitheredAlphaMask(int height, @ColorInt int[] colors, float[] positions) { DisplayMetrics dm = mActivity.getResources().getDisplayMetrics(); - int width = ResourceUtils.pxFromDp(ALPHA_MASK_WIDTH_DP, dm); - int gradientHeight = ResourceUtils.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm); - Bitmap dst = Bitmap.createBitmap(width, mMaskHeight, Bitmap.Config.ALPHA_8); + int width = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_WIDTH_DP, dm); + Bitmap dst = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8); Canvas c = new Canvas(dst); - Paint paint = new Paint(Paint.DITHER_FLAG); - LinearGradient lg = new LinearGradient(0, 0, 0, gradientHeight, - new int[]{ - 0x00FFFFFF, - setColorAlphaBound(Color.WHITE, (int) (0xFF * 0.95)), - 0xFFFFFFFF}, - new float[]{0f, 0.8f, 1f}, - Shader.TileMode.CLAMP); + Paint paint = new Paint(DITHER_FLAG); + LinearGradient lg = new LinearGradient(0, 0, 0, height, + colors, positions, Shader.TileMode.CLAMP); paint.setShader(lg); - c.drawRect(0, 0, width, gradientHeight, paint); + c.drawPaint(paint); return dst; } } diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java index bb7248fea7..17915394ae 100644 --- a/src/com/android/launcher3/icons/ShortcutCachingLogic.java +++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java @@ -107,7 +107,7 @@ public class ShortcutCachingLogic implements CachingLogic<ShortcutInfo> { try { return context.getSystemService(LauncherApps.class) .getShortcutIconDrawable(shortcutInfo, density); - } catch (SecurityException | IllegalStateException e) { + } catch (SecurityException | IllegalStateException | NullPointerException e) { Log.e(TAG, "Failed to get shortcut icon", e); return null; } diff --git a/src/com/android/launcher3/logging/KeyboardStateManager.java b/src/com/android/launcher3/logging/KeyboardStateManager.java index 6dc0a0be22..d0f9c7431b 100644 --- a/src/com/android/launcher3/logging/KeyboardStateManager.java +++ b/src/com/android/launcher3/logging/KeyboardStateManager.java @@ -24,7 +24,10 @@ import android.os.SystemClock; */ public class KeyboardStateManager { private long mUpdatedTime; - private int mImeHeight; + private int mImeHeightPx; + // Height of the keyboard when it's shown. + // mImeShownHeightPx>=mImeHeightPx always. + private int mImeShownHeightPx; public enum KeyboardState { NO_IME_ACTION, @@ -34,8 +37,9 @@ public class KeyboardStateManager { private KeyboardState mKeyboardState; - public KeyboardStateManager() { + public KeyboardStateManager(int defaultImeShownHeightPx) { mKeyboardState = NO_IME_ACTION; + mImeShownHeightPx = defaultImeShownHeightPx; } /** @@ -64,13 +68,25 @@ public class KeyboardStateManager { * Returns keyboard's current height. */ public int getImeHeight() { - return mImeHeight; + return mImeHeightPx; } /** - * Setter method to set keyboard height. + * Returns keyboard's height in pixels when shown. */ - public void setImeHeight(int imeHeight) { - mImeHeight = imeHeight; + public int getImeShownHeight() { + return mImeShownHeightPx; + } + + /** + * Setter method to set keyboard height in pixels. + */ + public void setImeHeight(int imeHeightPx) { + mImeHeightPx = imeHeightPx; + if (mImeHeightPx > 0) { + // Update the mImeShownHeightPx with the actual ime height when shown and store it + // for future sessions. + mImeShownHeightPx = mImeHeightPx; + } } } diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java index 66ed779eb5..265378c62d 100644 --- a/src/com/android/launcher3/logging/StatsLogManager.java +++ b/src/com/android/launcher3/logging/StatsLogManager.java @@ -32,12 +32,9 @@ import com.android.launcher3.logger.LauncherAtom.ContainerInfo; import com.android.launcher3.logger.LauncherAtom.FromState; import com.android.launcher3.logger.LauncherAtom.ToState; import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.util.IntArray; import com.android.launcher3.util.ResourceBasedOverride; import com.android.launcher3.views.ActivityContext; -import java.util.List; - /** * Handles the user event logging in R+. * @@ -59,6 +56,7 @@ public class StatsLogManager implements ResourceBasedOverride { private InstanceId mInstanceId; protected @Nullable ActivityContext mActivityContext = null; + protected @Nullable Context mContext = null; private KeyboardStateManager mKeyboardStateManager; /** @@ -623,6 +621,12 @@ public class StatsLogManager implements ResourceBasedOverride { @UiEvent(doc = "User has invoked split to left half with a keyboard shortcut.") LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP(1233), + @UiEvent(doc = "User has invoked split to right half from desktop mode.") + LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM(1412), + + @UiEvent(doc = "User has invoked split to left half from desktop mode.") + LAUNCHER_DESKTOP_MODE_SPLIT_LEFT_TOP(1464), + @UiEvent(doc = "User has collapsed the work FAB button by scrolling down in the all apps" + " work A-Z list.") LAUNCHER_WORK_FAB_BUTTON_COLLAPSE(1276), @@ -643,11 +647,17 @@ public class StatsLogManager implements ResourceBasedOverride { @UiEvent(doc = "User has swiped upwards from the gesture handle to show transient taskbar.") LAUNCHER_TRANSIENT_TASKBAR_SHOW(1331), + @UiEvent(doc = "User has clicked an app pair and launched directly into split screen.") + LAUNCHER_APP_PAIR_LAUNCH(1374), + + @UiEvent(doc = "User saved an app pair.") + LAUNCHER_APP_PAIR_SAVE(1456), + @UiEvent(doc = "App launched through pending intent") - LAUNCHER_APP_LAUNCH_PENDING_INTENT(1394), - ; + LAUNCHER_APP_LAUNCH_PENDING_INTENT(1394) // ADD MORE + ; private final int mId; @@ -831,6 +841,10 @@ public class StatsLogManager implements ResourceBasedOverride { */ public interface StatsLatencyLogger { + /** + * Should be in sync with: + * google3/wireless/android/sysui/aster/asterstats/launcher_event_processed.proto + */ enum LatencyType { UNKNOWN(0), // example: launcher restart that happens via daily backup and restore @@ -956,33 +970,32 @@ public class StatsLogManager implements ResourceBasedOverride { } /** - * Sets list of {@link com.android.app.search.ResultType} for the impression event. + * Sets {@link com.android.app.search.ResultType} for the impression event. */ - default StatsImpressionLogger withResultType(IntArray resultType) { + default StatsImpressionLogger withResultType(int resultType) { return this; } /** - * Sets list of count for each of {@link com.android.app.search.ResultType} for the - * impression event. + * Sets boolean for each of {@link com.android.app.search.ResultType} that indicates + * if this result is above keyboard or not for the impression event. */ - default StatsImpressionLogger withResultCount(IntArray resultCount) { + default StatsImpressionLogger withAboveKeyboard(boolean aboveKeyboard) { return this; } /** - * Sets list of boolean for each of {@link com.android.app.search.ResultType} that indicates - * if this result is above keyboard or not for the impression event. + * Sets uid for each of {@link com.android.app.search.ResultType} that indicates + * package name for the impression event. */ - default StatsImpressionLogger withAboveKeyboard(List<Boolean> aboveKeyboard) { + default StatsImpressionLogger withUid(int uid) { return this; } /** - * Sets list of uid for each of {@link com.android.app.search.ResultType} that indicates - * package name for the impression event. + * Sets result source that indicates the origin of the result for the impression event. */ - default StatsImpressionLogger withUids(IntArray uid) { + default StatsImpressionLogger withResultSource(int resultSource) { return this; } @@ -1031,7 +1044,9 @@ public class StatsLogManager implements ResourceBasedOverride { */ public KeyboardStateManager keyboardStateManager() { if (mKeyboardStateManager == null) { - mKeyboardStateManager = new KeyboardStateManager(); + mKeyboardStateManager = new KeyboardStateManager( + mContext != null ? mContext.getResources().getDimensionPixelSize( + R.dimen.default_ime_height) : 0); } return mKeyboardStateManager; } @@ -1067,6 +1082,7 @@ public class StatsLogManager implements ResourceBasedOverride { StatsLogManager manager = Overrides.getObject(StatsLogManager.class, context.getApplicationContext(), R.string.stats_log_manager_class); manager.mActivityContext = ActivityContext.lookupContextNoThrow(context); + manager.mContext = context; return manager; } } diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java index 27d1f78fb5..5e86bd6b8a 100644 --- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java +++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java @@ -91,8 +91,7 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { List<ItemInfo> filteredItems = new ArrayList<>(); for (Pair<ItemInfo, Object> entry : mItemList) { ItemInfo item = entry.first; - if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || - item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { + if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { // Short-circuit this logic if the icon exists somewhere on the workspace if (shortcutExists(dataModel, item.getIntent(), item.user)) { continue; diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java index 5d85b1cf6e..dbb29b8864 100644 --- a/src/com/android/launcher3/model/BaseLauncherBinder.java +++ b/src/com/android/launcher3/model/BaseLauncherBinder.java @@ -21,6 +21,7 @@ import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.os.Process; +import android.os.Trace; import android.util.Log; import com.android.launcher3.InvariantDeviceProfile; @@ -83,13 +84,18 @@ public abstract class BaseLauncherBinder { * Binds all loaded data to actual views on the main thread. */ public void bindWorkspace(boolean incrementBindId, boolean isBindSync) { - if (FeatureFlags.ENABLE_WORKSPACE_LOADING_OPTIMIZATION.get()) { - DisjointWorkspaceBinder workspaceBinder = + Trace.beginSection("BaseLauncherBinder#bindWorkspace"); + try { + if (FeatureFlags.ENABLE_WORKSPACE_LOADING_OPTIMIZATION.get()) { + DisjointWorkspaceBinder workspaceBinder = initWorkspaceBinder(incrementBindId, mBgDataModel.collectWorkspaceScreens()); - workspaceBinder.bindCurrentWorkspacePages(isBindSync); - workspaceBinder.bindOtherWorkspacePages(); - } else { - bindWorkspaceAllAtOnce(incrementBindId, isBindSync); + workspaceBinder.bindCurrentWorkspacePages(isBindSync); + workspaceBinder.bindOtherWorkspacePages(); + } else { + bindWorkspaceAllAtOnce(incrementBindId, isBindSync); + } + } finally { + Trace.endSection(); } } @@ -106,6 +112,7 @@ public abstract class BaseLauncherBinder { synchronized (mBgDataModel) { if (incrementBindId) { mBgDataModel.lastBindId++; + mBgDataModel.lastLoadId = mApp.getModel().getLastLoadId(); } mMyBindingId = mBgDataModel.lastBindId; return new DisjointWorkspaceBinder(workspacePages); @@ -126,6 +133,7 @@ public abstract class BaseLauncherBinder { mBgDataModel.extraItems.forEach(extraItems::add); if (incrementBindId) { mBgDataModel.lastBindId++; + mBgDataModel.lastLoadId = mApp.getModel().getLastLoadId(); } mMyBindingId = mBgDataModel.lastBindId; workspaceItemCount = mBgDataModel.itemsIdMap.size(); @@ -312,7 +320,8 @@ public abstract class BaseLauncherBinder { currentScreenIds, pendingTasks, workspaceItemCount, isBindSync); }, mUiExecutor); - mCallbacks.bindStringCache(mBgDataModel.stringCache.clone()); + StringCache cacheClone = mBgDataModel.stringCache.clone(); + executeCallbacksTask(c -> c.bindStringCache(cacheClone), pendingExecutor); } private void bindWorkspaceItems( @@ -440,9 +449,8 @@ public abstract class BaseLauncherBinder { .resumeModelPush(FLAG_LOADER_RUNNING); }); - for (Callbacks cb : mCallbacksList) { - cb.bindStringCache(mBgDataModel.stringCache.clone()); - } + StringCache cacheClone = mBgDataModel.stringCache.clone(); + executeCallbacksTask(c -> c.bindStringCache(cacheClone), mUiExecutor); } private void bindWorkspaceItems(final ArrayList<ItemInfo> workspaceItems) { diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java index 866e222592..97f540e1db 100644 --- a/src/com/android/launcher3/model/BaseModelUpdateTask.java +++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java @@ -15,9 +15,6 @@ */ package com.android.launcher3.model; -import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING; -import static com.android.launcher3.testing.shared.TestProtocol.testLogD; - import android.util.Log; import androidx.annotation.NonNull; @@ -33,7 +30,6 @@ import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; -import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.widget.model.WidgetsListBaseEntry; @@ -77,7 +73,6 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { @Override public final void run() { boolean isModelLoaded = Objects.requireNonNull(mModel).isModelLoaded(); - testLogD(WORK_TAB_MISSING, "modelLoaded: " + isModelLoaded + " forTask: " + this); if (!isModelLoaded) { if (DEBUG_TASKS) { Log.d(TAG, "Ignoring model task since loader is pending=" + this); @@ -115,10 +110,6 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { List<WorkspaceItemInfo> workspaceUpdates = allUpdates.stream() .filter(info -> info.id != ItemInfo.NO_ID) .collect(Collectors.toList()); - if (TestProtocol.sDebugTracing) { - Log.d(WORK_TAB_MISSING, "allUpdates: " + allUpdates.size() + ", workspaceUpdates " - + workspaceUpdates.size()); - } if (!workspaceUpdates.isEmpty()) { scheduleCallbackTask(c -> c.bindWorkspaceItemsChanged(workspaceUpdates)); } @@ -157,12 +148,7 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { } public void bindApplicationsIfNeeded() { - boolean changeFlag = mAllAppsList.getAndResetChangeFlag(); - if (TestProtocol.sDebugTracing) { - Log.d(WORK_TAB_MISSING, "bindApplicationsIfNeeded changeFlag? " + - changeFlag); - } - if (changeFlag) { + if (mAllAppsList.getAndResetChangeFlag()) { AppInfo[] apps = mAllAppsList.copyData(); int flags = mAllAppsList.getFlags(); Map<PackageUserKey, Integer> packageUserKeytoUidMap = Arrays.stream(apps).collect( diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java index d94df51c39..7bcd03863c 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -126,6 +126,11 @@ public class BgDataModel { public int lastBindId = 0; /** + * Load id for which the callbacks were successfully bound + */ + public int lastLoadId = -1; + + /** * Clears all the data */ public synchronized void clear() { @@ -211,7 +216,6 @@ public class BgDataModel { // Fall through. } case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: workspaceItems.remove(item); break; case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: @@ -246,7 +250,6 @@ public class BgDataModel { break; case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP || item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { workspaceItems.add(item); diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java index edc8c1bcd9..f24d1d2fff 100644 --- a/src/com/android/launcher3/model/DeviceGridState.java +++ b/src/com/android/launcher3/model/DeviceGridState.java @@ -33,6 +33,7 @@ import android.text.TextUtils; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.logging.StatsLogManager.LauncherEvent; +import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext; import java.util.Locale; import java.util.Objects; @@ -92,6 +93,9 @@ public class DeviceGridState implements Comparable<DeviceGridState> { * Stores the device state to shared preferences */ public void writeToPrefs(Context context) { + if (context instanceof SandboxContext) { + return; + } LauncherPrefs.get(context).put( WORKSPACE_SIZE.to(mGridSizeString), HOTSEAT_COUNT.to(mNumHotseat), diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java index 9a6cde63cd..c233872cc5 100644 --- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java +++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java @@ -45,7 +45,6 @@ import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.IntArray; -import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.WidgetManagerHelper; @@ -133,10 +132,8 @@ public class GridSizeMigrationUtil { Log.v(TAG, "Workspace migration completed in " + (System.currentTimeMillis() - migrationStartTime)); - if (!(context instanceof SandboxContext)) { - // Save current configuration, so that the migration does not run again. - destDeviceState.writeToPrefs(context); - } + // Save current configuration, so that the migration does not run again. + destDeviceState.writeToPrefs(context); } } @@ -455,7 +452,6 @@ public class GridSizeMigrationUtil { try { // calculate weight switch (entry.itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: { entry.mIntent = c.getString(indexIntent); @@ -531,7 +527,6 @@ public class GridSizeMigrationUtil { try { // calculate weight switch (entry.itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: { entry.mIntent = c.getString(indexIntent); diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java index fa0511cc56..9a3abd47fd 100644 --- a/src/com/android/launcher3/model/ItemInstallQueue.java +++ b/src/com/android/launcher3/model/ItemInstallQueue.java @@ -286,7 +286,6 @@ public class ItemInstallQueue { final WorkspaceItemInfo si = new WorkspaceItemInfo(); si.user = user; - si.itemType = ITEM_TYPE_APPLICATION; LauncherActivityInfo lai; boolean usePackageIcon = laiList.isEmpty(); diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java index 2054d930e6..33332f01ed 100644 --- a/src/com/android/launcher3/model/LoaderCursor.java +++ b/src/com/android/launcher3/model/LoaderCursor.java @@ -193,9 +193,7 @@ public class LoaderCursor extends CursorWrapper { public IconRequestInfo<WorkspaceItemInfo> createIconRequestInfo( WorkspaceItemInfo wai, boolean useLowResIcon) { - byte[] iconBlob = itemType == Favorites.ITEM_TYPE_SHORTCUT - || itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT - || restoreFlag != 0 + byte[] iconBlob = itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT || restoreFlag != 0 ? getIconBlob() : null; return new IconRequestInfo<>(wai, mActivityInfo, iconBlob, useLowResIcon); @@ -347,7 +345,6 @@ public class LoaderCursor extends CursorWrapper { } final WorkspaceItemInfo info = new WorkspaceItemInfo(); - info.itemType = Favorites.ITEM_TYPE_APPLICATION; info.user = user; info.intent = newIntent; diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index ca356b08b5..0e68db29e8 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -24,8 +24,6 @@ import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED; -import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING; -import static com.android.launcher3.testing.shared.TestProtocol.testLogD; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission; import static com.android.launcher3.util.PackageManagerHelper.isSystemApp; @@ -33,7 +31,6 @@ import static com.android.launcher3.util.PackageManagerHelper.isSystemApp; import android.annotation.SuppressLint; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -61,7 +58,6 @@ import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings.Favorites; -import com.android.launcher3.LauncherSettings.Settings; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; @@ -203,7 +199,7 @@ public class LoaderTask implements Runnable { } } - Object traceToken = TraceHelper.INSTANCE.beginSection(TAG); + TraceHelper.INSTANCE.beginSection(TAG); LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger(); try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { List<ShortcutInfo> allShortcuts = new ArrayList<>(); @@ -327,7 +323,7 @@ public class LoaderTask implements Runnable { memoryLogger.printLogs(); throw e; } - TraceHelper.INSTANCE.endSection(traceToken); + TraceHelper.INSTANCE.endSection(); } public synchronized void stopLocked() { @@ -361,25 +357,15 @@ public class LoaderTask implements Runnable { String selection, @Nullable LoaderMemoryLogger memoryLogger) { final Context context = mApp.getContext(); - final ContentResolver contentResolver = context.getContentResolver(); final PackageManagerHelper pmHelper = new PackageManagerHelper(context); final boolean isSafeMode = pmHelper.isSafeMode(); final boolean isSdCardReady = Utilities.isBootCompleted(); final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context); - boolean clearDb = false; - if (!mApp.getModel().getModelDbController().migrateGridIfNeeded()) { - // Migration failed. Clear workspace. - clearDb = true; - } - - if (clearDb) { - Log.d(TAG, "loadWorkspace: resetting launcher database"); - Settings.call(contentResolver, Settings.METHOD_CREATE_EMPTY_DB); - } - + ModelDbController dbController = mApp.getModel().getModelDbController(); + dbController.tryMigrateDB(); Log.d(TAG, "loadWorkspace: loading default favorites"); - Settings.call(contentResolver, Settings.METHOD_LOAD_DEFAULT_FAVORITES); + dbController.loadDefaultFavoritesIfNecessary(); synchronized (mBgDataModel) { mBgDataModel.clear(); @@ -388,17 +374,18 @@ public class LoaderTask implements Runnable { final HashMap<PackageUserKey, SessionInfo> installingPkgs = mSessionHelper.getActiveSessions(); installingPkgs.forEach(mApp.getIconCache()::updateSessionCache); + FileLog.d(TAG, "loadWorkspace: Packages with active install sessions: " + + installingPkgs.values()); final PackageUserKey tempPackageKey = new PackageUserKey(null, null); mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs); mShortcutKeyToPinnedShortcuts = new HashMap<>(); - ModelDbController dbController = mApp.getModel().getModelDbController(); final LoaderCursor c = new LoaderCursor( dbController.query(TABLE_NAME, null, selection, null, null), mApp, mUserManagerState); final Bundle extras = c.getExtras(); - mDbName = extras == null ? null : extras.getString(Settings.EXTRA_DB_NAME); + mDbName = extras == null ? null : extras.getString(ModelDbController.EXTRA_DB_NAME); try { final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>(); @@ -505,7 +492,6 @@ public class LoaderTask implements Runnable { boolean allowMissingTarget = false; switch (c.itemType) { - case Favorites.ITEM_TYPE_SHORTCUT: case Favorites.ITEM_TYPE_APPLICATION: case Favorites.ITEM_TYPE_DEEP_SHORTCUT: Intent intent = c.parseIntent(); @@ -519,9 +505,8 @@ public class LoaderTask implements Runnable { ComponentName cn = intent.getComponent(); String targetPkg = cn == null ? intent.getPackage() : cn.getPackageName(); - if (TextUtils.isEmpty(targetPkg) - && c.itemType != Favorites.ITEM_TYPE_SHORTCUT) { - c.markDeleted("Only legacy shortcuts can have null package"); + if (TextUtils.isEmpty(targetPkg)) { + c.markDeleted("Shortcuts can't have null package"); return; } @@ -707,9 +692,11 @@ public class LoaderTask implements Runnable { break; case Favorites.ITEM_TYPE_FOLDER: + case Favorites.ITEM_TYPE_APP_PAIR: FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id); c.applyCommonProperties(folderInfo); + folderInfo.itemType = c.itemType; // Do not trim the folder label, as is was set by the user. folderInfo.title = c.getString(c.mTitleIndex); folderInfo.spanX = 1; @@ -917,9 +904,7 @@ public class LoaderTask implements Runnable { private void sanitizeFolders(boolean itemsDeleted) { if (itemsDeleted) { // Remove any empty folder - int[] deletedFolderIds = Settings.call(mApp.getContext().getContentResolver(), - Settings.METHOD_DELETE_EMPTY_FOLDERS) - .getIntArray(Settings.EXTRA_VALUE); + IntArray deletedFolderIds = mApp.getModel().getModelDbController().deleteEmptyFolders(); synchronized (mBgDataModel) { for (int folderId : deletedFolderIds) { mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId)); @@ -932,11 +917,9 @@ public class LoaderTask implements Runnable { private void sanitizeWidgetsShortcutsAndPackages() { Context context = mApp.getContext(); - ContentResolver contentResolver = context.getContentResolver(); // Remove any ghost widgets - Settings.call(contentResolver, - Settings.METHOD_REMOVE_GHOST_WIDGETS); + mApp.getModel().getModelDbController().removeGhostWidgets(); // Update pinned state of model shortcuts mBgDataModel.updateShortcutPinnedState(context); @@ -951,7 +934,6 @@ public class LoaderTask implements Runnable { } private List<LauncherActivityInfo> loadAllApps() { - testLogD(WORK_TAB_MISSING, "loadingAllApps"); final List<UserHandle> profiles = mUserCache.getUserProfiles(); List<LauncherActivityInfo> allActivityList = new ArrayList<>(); // Clear the list of apps diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java index f0e5ef6bbd..e10e72d61b 100644 --- a/src/com/android/launcher3/model/ModelDbController.java +++ b/src/com/android/launcher3/model/ModelDbController.java @@ -37,7 +37,6 @@ import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; -import android.os.Binder; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.Process; @@ -85,6 +84,7 @@ public class ModelDbController { private static final String TAG = "LauncherProvider"; private static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED"; + public static final String EXTRA_DB_NAME = "db_name"; protected DatabaseHelper mOpenHelper; @@ -140,7 +140,7 @@ public class ModelDbController { table, projection, selection, selectionArgs, null, null, sortOrder); final Bundle extra = new Bundle(); - extra.putString(LauncherSettings.Settings.EXTRA_DB_NAME, mOpenHelper.getDatabaseName()); + extra.putString(EXTRA_DB_NAME, mOpenHelper.getDatabaseName()); result.setExtras(extra); return result; } @@ -162,28 +162,6 @@ public class ModelDbController { } /** - * Similar to insert but for adding multiple values in a transaction. - */ - @WorkerThread - public int bulkInsert(String table, ContentValues[] values) { - createDbIfNotExists(); - - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - try (SQLiteTransaction t = new SQLiteTransaction(db)) { - int numValues = values.length; - for (int i = 0; i < numValues; i++) { - addModifiedTime(values[i]); - if (mOpenHelper.dbInsertAndCheck(db, table, values[i]) < 0) { - return 0; - } - } - onAddOrDeleteOp(db); - t.commit(); - } - return values.length; - } - - /** * Refer {@link SQLiteDatabase#delete(String, String, String[])} */ @WorkerThread @@ -191,10 +169,6 @@ public class ModelDbController { createDbIfNotExists(); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - if (Binder.getCallingPid() != Process.myPid() - && Favorites.TABLE_NAME.equalsIgnoreCase(table)) { - mOpenHelper.removeGhostWidgets(mOpenHelper.getWritableDatabase()); - } int count = db.delete(table, selection, selectionArgs); if (count > 0) { onAddOrDeleteOp(db); @@ -250,6 +224,7 @@ public class ModelDbController { public void createEmptyDB() { createDbIfNotExists(); mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase()); + LauncherPrefs.get(mContext).putSync(getEmptyDbCreatedKey().to(true)); } /** @@ -280,14 +255,34 @@ public class ModelDbController { mOpenHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE); } + + /** + * Migrates the DB if needed. If the migration failed, it clears the DB. + */ + public void tryMigrateDB() { + if (!migrateGridIfNeeded()) { + Log.d(TAG, "Migration failed: resetting launcher database"); + createEmptyDB(); + LauncherPrefs.get(mContext).putSync( + getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()).to(true)); + + // Write the grid state to avoid another migration + new DeviceGridState(LauncherAppState.getIDP(mContext)).writeToPrefs(mContext); + } + } + /** * Migrates the DB if needed, and returns false if the migration failed * and DB needs to be cleared. * @return true if migration was success or ignored, false if migration failed * and the DB should be reset. */ - public boolean migrateGridIfNeeded() { + private boolean migrateGridIfNeeded() { createDbIfNotExists(); + if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey())) { + // If we have already create a new DB, ignore migration + return false; + } InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext); if (!GridSizeMigrationUtil.needsToMigrate(mContext, idp)) { return true; @@ -358,7 +353,7 @@ public class ModelDbController { } private void clearFlagEmptyDbCreated() { - LauncherPrefs.get(mContext).removeSync(getEmptyDbCreatedKey(mOpenHelper.getDatabaseName())); + LauncherPrefs.get(mContext).removeSync(getEmptyDbCreatedKey()); } /** @@ -372,7 +367,7 @@ public class ModelDbController { public synchronized void loadDefaultFavoritesIfNecessary() { createDbIfNotExists(); - if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()))) { + if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey())) { Log.d(TAG, "loading default workspace"); LauncherWidgetHolder widgetHolder = mOpenHelper.newLauncherWidgetHolder(); @@ -471,7 +466,7 @@ public class ModelDbController { () -> parser, AutoInstallsLayout.TAG_WORKSPACE); } - private static Uri getLayoutUri(String authority, Context ctx) { + public static Uri getLayoutUri(String authority, Context ctx) { InvariantDeviceProfile grid = LauncherAppState.getIDP(ctx); return new Uri.Builder().scheme("content").authority(authority).path("launcher_layout") .appendQueryParameter("version", "1") @@ -491,6 +486,10 @@ public class ModelDbController { mOpenHelper, mContext.getResources(), defaultLayout); } + private ConstantItem<Boolean> getEmptyDbCreatedKey() { + return getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()); + } + /** * Re-composite given key in respect to database. If the current db is * {@link LauncherFiles#LAUNCHER_DB}, return the key as-is. Otherwise append the db name to diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java index ddb8b05839..2358a9fc5f 100644 --- a/src/com/android/launcher3/model/ModelWriter.java +++ b/src/com/android/launcher3/model/ModelWriter.java @@ -16,13 +16,12 @@ package com.android.launcher3.model; +import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; +import static com.android.launcher3.provider.LauncherDbUtils.itemIdMatch; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; -import android.content.ContentProviderOperation; -import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; -import android.net.Uri; import android.text.TextUtils; import android.util.Log; @@ -32,9 +31,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherModel.CallbackTask; -import com.android.launcher3.LauncherProvider; import com.android.launcher3.LauncherSettings.Favorites; -import com.android.launcher3.LauncherSettings.Settings; import com.android.launcher3.Utilities; import com.android.launcher3.celllayout.CellPosMapper; import com.android.launcher3.celllayout.CellPosMapper.CellPos; @@ -45,6 +42,7 @@ import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; import com.android.launcher3.util.ContentWriter; import com.android.launcher3.util.Executors; import com.android.launcher3.util.ItemInfoMatcher; @@ -80,7 +78,7 @@ public class ModelWriter { private final boolean mVerifyChanges; // Keep track of delete operations that occur when an Undo option is present; we may not commit. - private final List<Runnable> mDeleteRunnables = new ArrayList<>(); + private final List<ModelTask> mDeleteRunnables = new ArrayList<>(); private boolean mPreparingToUndo; private final CellPosMapper mCellPosMapper; @@ -217,8 +215,7 @@ public class ModelWriter { item.spanX = spanX; item.spanY = spanY; notifyItemModified(item); - - MODEL_EXECUTOR.execute(new UpdateItemRunnable(item, () -> + new UpdateItemRunnable(item, () -> new ContentWriter(mContext) .put(Favorites.CONTAINER, item.container) .put(Favorites.CELLX, item.cellX) @@ -226,7 +223,8 @@ public class ModelWriter { .put(Favorites.RANK, item.rank) .put(Favorites.SPANX, item.spanX) .put(Favorites.SPANY, item.spanY) - .put(Favorites.SCREEN, item.screenId))); + .put(Favorites.SCREEN, item.screenId)) + .executeOnModelThread(); } /** @@ -234,11 +232,11 @@ public class ModelWriter { */ public void updateItemInDatabase(ItemInfo item) { notifyItemModified(item); - MODEL_EXECUTOR.execute(new UpdateItemRunnable(item, () -> { + new UpdateItemRunnable(item, () -> { ContentWriter writer = new ContentWriter(mContext); item.onAddToDatabase(writer); return writer; - })); + }).executeOnModelThread(); } private void notifyItemModified(ItemInfo item) { @@ -253,13 +251,12 @@ public class ModelWriter { int container, int screenId, int cellX, int cellY) { updateItemInfoProps(item, container, screenId, cellX, cellY); - final ContentResolver cr = mContext.getContentResolver(); - item.id = Settings.call(cr, Settings.METHOD_NEW_ITEM_ID).getInt(Settings.EXTRA_VALUE); + item.id = mModel.getModelDbController().generateNewItemId(); notifyOtherCallbacks(c -> c.bindItems(Collections.singletonList(item), false)); ModelVerifier verifier = new ModelVerifier(); final StackTraceElement[] stackTrace = new Throwable().getStackTrace(); - MODEL_EXECUTOR.execute(() -> { + newModelTask(() -> { // Write the item on background thread, as some properties might have been updated in // the background. final ContentWriter writer = new ContentWriter(mContext); @@ -272,7 +269,7 @@ public class ModelWriter { mBgDataModel.addItem(mContext, item, true); verifier.verifyModel(); } - }); + }).executeOnModelThread(); } /** @@ -303,15 +300,13 @@ public class ModelWriter { Collectors.joining(",")) + ". Reason: [" + (TextUtils.isEmpty(reason) ? "unknown" : reason) + "]"); notifyDelete(items); - enqueueDeleteRunnable(() -> { + enqueueDeleteRunnable(newModelTask(() -> { for (ItemInfo item : items) { - final Uri uri = Favorites.getContentUri(item.id); - mContext.getContentResolver().delete(uri, null, null); - + mModel.getModelDbController().delete(TABLE_NAME, itemIdMatch(item.id), null); mBgDataModel.removeItem(mContext, item); verifier.verifyModel(); } - }); + })); } /** @@ -321,7 +316,7 @@ public class ModelWriter { ModelVerifier verifier = new ModelVerifier(); notifyDelete(Collections.singleton(info)); - enqueueDeleteRunnable(() -> { + enqueueDeleteRunnable(newModelTask(() -> { mModel.getModelDbController().delete(Favorites.TABLE_NAME, Favorites.CONTAINER + "=" + info.id, null); mBgDataModel.removeItem(mContext, info.contents); @@ -331,7 +326,7 @@ public class ModelWriter { Favorites._ID + "=" + info.id, null); mBgDataModel.removeItem(mContext, info); verifier.verifyModel(); - }); + })); } /** @@ -343,7 +338,7 @@ public class ModelWriter { if (holder != null && !info.isCustomWidget() && info.isWidgetIdAllocated()) { // Deleting an app widget ID is a void call but writes to disk before returning // to the caller... - enqueueDeleteRunnable(() -> holder.deleteAppWidgetId(info.appWidgetId)); + enqueueDeleteRunnable(newModelTask(() -> holder.deleteAppWidgetId(info.appWidgetId))); } deleteItemFromDatabase(info, reason); } @@ -373,19 +368,17 @@ public class ModelWriter { * {@link #commitDelete()} is called (or abandoned if {@link #abortDelete} is called). * Otherwise, we run the Runnable immediately. */ - private void enqueueDeleteRunnable(Runnable r) { + private void enqueueDeleteRunnable(ModelTask r) { if (mPreparingToUndo) { mDeleteRunnables.add(r); } else { - MODEL_EXECUTOR.execute(r); + r.executeOnModelThread(); } } public void commitDelete() { mPreparingToUndo = false; - for (Runnable runnable : mDeleteRunnables) { - MODEL_EXECUTOR.execute(runnable); - } + mDeleteRunnables.forEach(ModelTask::executeOnModelThread); mDeleteRunnables.clear(); } @@ -426,10 +419,9 @@ public class ModelWriter { } @Override - public void run() { - Uri uri = Favorites.getContentUri(mItemId); - mContext.getContentResolver().update(uri, mWriter.get().getValues(mContext), - null, null); + public void runImpl() { + mModel.getModelDbController().update( + TABLE_NAME, mWriter.get().getValues(mContext), itemIdMatch(mItemId), null); updateItemArrays(mItem, mItemId); } } @@ -444,27 +436,24 @@ public class ModelWriter { } @Override - public void run() { - ArrayList<ContentProviderOperation> ops = new ArrayList<>(); - int count = mItems.size(); - for (int i = 0; i < count; i++) { - ItemInfo item = mItems.get(i); - final int itemId = item.id; - final Uri uri = Favorites.getContentUri(itemId); - ContentValues values = mValues.get(i); - - ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build()); - updateItemArrays(item, itemId); - } - try { - mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, ops); + public void runImpl() { + try (SQLiteTransaction t = mModel.getModelDbController().newTransaction()) { + int count = mItems.size(); + for (int i = 0; i < count; i++) { + ItemInfo item = mItems.get(i); + final int itemId = item.id; + mModel.getModelDbController().update( + TABLE_NAME, mValues.get(i), itemIdMatch(itemId), null); + updateItemArrays(item, itemId); + } + t.commit(); } catch (Exception e) { e.printStackTrace(); } } } - private abstract class UpdateItemBaseRunnable implements Runnable { + private abstract class UpdateItemBaseRunnable extends ModelTask { private final StackTraceElement[] mStackTrace; private final ModelVerifier mVerifier = new ModelVerifier(); @@ -498,9 +487,9 @@ public class ModelWriter { modelItem.container == Favorites.CONTAINER_HOTSEAT)) { switch (modelItem.itemType) { case Favorites.ITEM_TYPE_APPLICATION: - case Favorites.ITEM_TYPE_SHORTCUT: case Favorites.ITEM_TYPE_DEEP_SHORTCUT: case Favorites.ITEM_TYPE_FOLDER: + case Favorites.ITEM_TYPE_APP_PAIR: if (!mBgDataModel.workspaceItems.contains(modelItem)) { mBgDataModel.workspaceItems.add(modelItem); } @@ -516,6 +505,35 @@ public class ModelWriter { } } + private abstract class ModelTask implements Runnable { + + private final int mLoadId = mBgDataModel.lastLoadId; + + @Override + public final void run() { + if (mLoadId != mModel.getLastLoadId()) { + Log.d(TAG, "Model changed before the task could execute"); + return; + } + runImpl(); + } + + public final void executeOnModelThread() { + MODEL_EXECUTOR.execute(this); + } + + public abstract void runImpl(); + } + + private ModelTask newModelTask(Runnable r) { + return new ModelTask() { + @Override + public void runImpl() { + r.run(); + } + }; + } + /** * Utility class to verify model updates are propagated properly to the callback. */ diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index 8c938f4124..2591550765 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -93,11 +93,6 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { mOp = op; mUser = user; mPackages = packages; - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.WORK_TAB_MISSING, "PackageUpdatedTask mOp: " + mOp + - " packageCount: " + mPackages.length + " user: " + user); - DEBUG = true; - } } @Override @@ -142,9 +137,6 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { // The update may have changed which shortcuts/widgets are available. // Refresh the widgets for the package if we have an activity running. Launcher launcher = Launcher.ACTIVITY_TRACKER.getCreatedActivity(); - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.WORK_TAB_MISSING, "launcher: " + launcher); - } if (launcher != null) { launcher.refreshAndBindWidgetsForPackageUser( new PackageUserKey(packages[i], mUser)); diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java index cb78138459..63ca35b79c 100644 --- a/src/com/android/launcher3/model/UserLockStateChangedTask.java +++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java @@ -16,12 +16,10 @@ package com.android.launcher3.model; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER; -import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING; import android.content.Context; import android.content.pm.ShortcutInfo; import android.os.UserHandle; -import android.util.Log; import androidx.annotation.NonNull; @@ -31,7 +29,6 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.shortcuts.ShortcutRequest; import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult; -import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.ItemInfoMatcher; @@ -63,10 +60,6 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask { if (mIsUserUnlocked) { QueryResult shortcuts = new ShortcutRequest(context, mUser) .query(ShortcutRequest.PINNED); - if (TestProtocol.sDebugTracing) { - Log.d(WORK_TAB_MISSING, "shortcutQuery success? " - + shortcuts.wasSuccess()); - } if (shortcuts.wasSuccess()) { for (ShortcutInfo shortcut : shortcuts) { pinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut); @@ -89,9 +82,6 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask { if (mIsUserUnlocked) { ShortcutKey key = ShortcutKey.fromItemInfo(si); ShortcutInfo shortcut = pinnedShortcuts.get(key); - if (TestProtocol.sDebugTracing) { - Log.d(WORK_TAB_MISSING, "shortcutInfo: " + shortcut); - } // We couldn't verify the shortcut during loader. If its no longer available // (probably due to clear data), delete the workspace item as well if (shortcut == null) { diff --git a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java index 93fc6a539f..1fc8a03a83 100644 --- a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java +++ b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java @@ -82,9 +82,7 @@ public class WorkspaceItemSpaceFinder { if (!found) { // Still no position found. Add a new screen to the end. - screenId = LauncherSettings.Settings.call(app.getContext().getContentResolver(), - LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) - .getInt(LauncherSettings.Settings.EXTRA_VALUE); + screenId = app.getModel().getModelDbController().getNewScreenId(); // Save the screen id for binding in the workspace workspaceScreens.add(screenId); diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java index e5a0eb1996..9bf6d43b17 100644 --- a/src/com/android/launcher3/model/data/FolderInfo.java +++ b/src/com/android/launcher3/model/data/FolderInfo.java @@ -119,8 +119,8 @@ public class FolderInfo extends ItemInfo { public static FolderInfo createAppPair(WorkspaceItemInfo app1, WorkspaceItemInfo app2) { FolderInfo newAppPair = new FolderInfo(); newAppPair.itemType = LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR; - newAppPair.contents.add(app1); - newAppPair.contents.add(app2); + newAppPair.add(app1, /* animate */ false); + newAppPair.add(app2, /* animate */ false); return newAppPair; } diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java index 660929c566..9afa4593a9 100644 --- a/src/com/android/launcher3/model/data/ItemInfo.java +++ b/src/com/android/launcher3/model/data/ItemInfo.java @@ -30,7 +30,6 @@ import static com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINE import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; -import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK; import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET; import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID; @@ -50,7 +49,6 @@ import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Animation; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.Workspace; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.logger.LauncherAtom.AllAppsContainer; import com.android.launcher3.logger.LauncherAtom.ContainerInfo; @@ -88,7 +86,6 @@ public class ItemInfo { /** * One of {@link Favorites#ITEM_TYPE_APPLICATION}, - * {@link Favorites#ITEM_TYPE_SHORTCUT}, * {@link Favorites#ITEM_TYPE_DEEP_SHORTCUT} * {@link Favorites#ITEM_TYPE_FOLDER}, * {@link Favorites#ITEM_TYPE_APP_PAIR}, @@ -325,9 +322,7 @@ public class ItemInfo { * Returns whether this item should use the background animation. */ public boolean shouldUseBackgroundAnimation() { - return animationType == LauncherSettings.Animation.VIEW_BACKGROUND - && FeatureFlags.ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES.get() - && FeatureFlags.ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION.get(); + return animationType == LauncherSettings.Animation.VIEW_BACKGROUND; } /** @@ -368,13 +363,6 @@ public class ItemInfo { }) .orElse(LauncherAtom.Shortcut.newBuilder())); break; - case ITEM_TYPE_SHORTCUT: - itemBuilder - .setShortcut(nullableComponent - .map(component -> LauncherAtom.Shortcut.newBuilder() - .setShortcutName(component.flattenToShortString())) - .orElse(LauncherAtom.Shortcut.newBuilder())); - break; case ITEM_TYPE_APPWIDGET: itemBuilder .setWidget(nullableComponent diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java index e5fb015f2e..b4a935a74a 100644 --- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java +++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java @@ -119,6 +119,11 @@ public abstract class ItemInfoWithIcon extends ItemInfo { | FLAG_DISABLED_VERSION_LOWER; /** + * Flag indicating this item can't be pinned to home screen. + */ + public static final int FLAG_NOT_PINNABLE = 1 << 13; + + /** * Status associated with the system state of the underlying item. This is calculated every * time a new info is created and not persisted on the disk. */ diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java index 01606d4611..3ce194dd81 100644 --- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java +++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java @@ -96,7 +96,7 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon { public WorkspaceItemInfo() { - itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; + itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; } public WorkspaceItemInfo(WorkspaceItemInfo info) { @@ -205,8 +205,8 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon { @Override public ComponentName getTargetComponent() { ComponentName cn = super.getTargetComponent(); - if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT || hasStatusFlag( - FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON | FLAG_RESTORED_ICON))) { + if (cn == null && hasStatusFlag( + FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON | FLAG_RESTORED_ICON)) { // Legacy shortcuts and promise icons with web UI may not have a componentName but just // a packageName. In that case create a empty componentName instead of adding additional // check everywhere. diff --git a/src/com/android/launcher3/notification/NotificationContainer.java b/src/com/android/launcher3/notification/NotificationContainer.java index 9eb05cd40f..7cc9ad38bc 100644 --- a/src/com/android/launcher3/notification/NotificationContainer.java +++ b/src/com/android/launcher3/notification/NotificationContainer.java @@ -15,7 +15,7 @@ */ package com.android.launcher3.notification; -import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; +import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL; import android.animation.Animator; diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java index bb2c37f131..f4468fdf01 100644 --- a/src/com/android/launcher3/notification/NotificationInfo.java +++ b/src/com/android/launcher3/notification/NotificationInfo.java @@ -18,6 +18,7 @@ package com.android.launcher3.notification; import static com.android.launcher3.AbstractFloatingView.TYPE_ACTION_POPUP; import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS; +import static com.android.launcher3.Utilities.allowBGLaunch; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_LAUNCH_TAP; import android.app.ActivityOptions; @@ -26,7 +27,6 @@ import android.app.PendingIntent; import android.content.Context; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; -import android.os.Bundle; import android.service.notification.StatusBarNotification; import android.view.View; @@ -103,10 +103,10 @@ public class NotificationInfo implements View.OnClickListener { return; } final ActivityContext context = ActivityContext.lookupContext(view.getContext()); - Bundle activityOptions = ActivityOptions.makeClipRevealAnimation( - view, 0, 0, view.getWidth(), view.getHeight()).toBundle(); + ActivityOptions options = allowBGLaunch(ActivityOptions.makeClipRevealAnimation( + view, 0, 0, view.getWidth(), view.getHeight())); try { - intent.send(null, 0, null, null, null, null, activityOptions); + intent.send(null, 0, null, null, null, null, options.toBundle()); context.getStatsLogManager().logger().withItemInfo(mItemInfo) .log(LAUNCHER_NOTIFICATION_LAUNCH_TAP); } catch (PendingIntent.CanceledException e) { diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java index 16a40576b8..ecd018b26d 100644 --- a/src/com/android/launcher3/notification/NotificationMainView.java +++ b/src/com/android/launcher3/notification/NotificationMainView.java @@ -16,8 +16,8 @@ package com.android.launcher3.notification; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.Utilities.mapToRange; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DISMISSED; import android.animation.AnimatorSet; diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java index 570d6ff267..4ab20373b4 100644 --- a/src/com/android/launcher3/pageindicators/PageIndicator.java +++ b/src/com/android/launcher3/pageindicators/PageIndicator.java @@ -27,6 +27,14 @@ public interface PageIndicator { void setMarkersCount(int numMarkers); /** + * Sets flag to indicate when the screens are in the process of binding so that we don't animate + * during that period. + */ + default void setAreScreensBinding(boolean areScreensBinding) { + // No-op by default + } + + /** * Sets the flag if the Page Indicator should autohide. */ default void setShouldAutoHide(boolean shouldAutoHide) { diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java index b2c64b3a0d..ce71275001 100644 --- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java +++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java @@ -130,6 +130,7 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator */ private float mCurrentPosition; private float mFinalPosition; + private boolean mAreScreensBinding; private ObjectAnimator mAnimator; private @Nullable ObjectAnimator mAlphaAnimator; @@ -163,7 +164,7 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator @Override public void setScroll(int currentScroll, int totalScroll) { - if (SHOW_DOT_PAGINATION.get() && mActivePage != 0 && currentScroll == 0) { + if (SHOW_DOT_PAGINATION.get() && currentScroll == 0 && totalScroll == 0) { CURRENT_POSITION.set(this, (float) mActivePage); return; } @@ -172,6 +173,11 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator return; } + // Skip scroll update during binding. We will update it when binding completes. + if (mAreScreensBinding) { + return; + } + if (mShouldAutoHide) { animatePaginationToAlpha(VISIBLE_ALPHA); } @@ -359,6 +365,16 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator } @Override + public void setAreScreensBinding(boolean areScreensBinding) { + // Reapply correct current position which was skipped during setScroll. + if (mAreScreensBinding && !areScreensBinding) { + CURRENT_POSITION.set(this, (float) mActivePage); + } + + mAreScreensBinding = areScreensBinding; + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Add extra spacing of mDotRadius on all sides so than entry animation could be run. int width = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY ? @@ -381,7 +397,9 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator // Draw all page indicators; float circleGap = mCircleGap; - float startX = (getWidth() - (mNumPages * circleGap) + mDotRadius) / 2; + float startX = ((float) getWidth() / 2) + - (mCircleGap * (((float) mNumPages - 1) / 2)) + - mDotRadius; float x = startX + mDotRadius; float y = getHeight() / 2; @@ -420,9 +438,9 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator float startCircle = (int) mCurrentPosition; float delta = mCurrentPosition - startCircle; float diameter = 2 * mDotRadius; - float startX; - - startX = ((getWidth() - (mNumPages * mCircleGap) + mDotRadius) / 2); + float startX = ((float) getWidth() / 2) + - (mCircleGap * (((float) mNumPages - 1) / 2)) + - mDotRadius; sTempRect.top = (getHeight() * 0.5f) - mDotRadius; sTempRect.bottom = (getHeight() * 0.5f) + mDotRadius; sTempRect.left = startX + (startCircle * mCircleGap); diff --git a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java index b24ee34502..351ebce087 100644 --- a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java +++ b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java @@ -16,8 +16,11 @@ package com.android.launcher3.pm; +import static com.android.launcher3.Utilities.allowBGLaunch; + import android.annotation.TargetApi; import android.app.Activity; +import android.app.ActivityOptions; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; @@ -72,7 +75,7 @@ public abstract class ShortcutConfigActivityInfo implements ComponentWithLabelAn } public int getItemType() { - return LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; + return LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; } @Override @@ -138,8 +141,10 @@ public abstract class ShortcutConfigActivityInfo implements ComponentWithLabelAn } IntentSender is = activity.getSystemService(LauncherApps.class) .getShortcutConfigActivityIntent(mInfo); + ActivityOptions options = allowBGLaunch(ActivityOptions.makeBasic()); try { - activity.startIntentSenderForResult(is, requestCode, null, 0, 0, 0); + activity.startIntentSenderForResult(is, requestCode, null, 0, 0, 0, + options.toBundle()); return true; } catch (IntentSender.SendIntentException e) { Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java index 24a9609a83..92822ab79a 100644 --- a/src/com/android/launcher3/pm/UserCache.java +++ b/src/com/android/launcher3/pm/UserCache.java @@ -16,16 +16,19 @@ package com.android.launcher3.pm; -import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING; -import static com.android.launcher3.testing.shared.TestProtocol.sDebugTracing; -import static com.android.launcher3.testing.shared.TestProtocol.testLogD; +import static com.android.launcher3.Utilities.ATLEAST_U; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.content.Context; import android.content.Intent; +import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; -import android.util.LongSparseArray; + +import androidx.annotation.AnyThread; +import androidx.annotation.NonNull; +import androidx.annotation.WorkerThread; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.SafeCloseable; @@ -34,136 +37,121 @@ import com.android.launcher3.util.SimpleBroadcastReceiver; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; /** * Class which manages a local cache of user handles to avoid system rpc */ -public class UserCache { +public class UserCache implements SafeCloseable { + + public static final String ACTION_PROFILE_ADDED = ATLEAST_U + ? Intent.ACTION_PROFILE_ADDED : Intent.ACTION_MANAGED_PROFILE_ADDED; + public static final String ACTION_PROFILE_REMOVED = ATLEAST_U + ? Intent.ACTION_PROFILE_REMOVED : Intent.ACTION_MANAGED_PROFILE_REMOVED; + + public static final String ACTION_PROFILE_UNLOCKED = ATLEAST_U + ? Intent.ACTION_PROFILE_ACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNLOCKED; + public static final String ACTION_PROFILE_LOCKED = ATLEAST_U + ? Intent.ACTION_PROFILE_INACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE; public static final MainThreadInitializedObject<UserCache> INSTANCE = new MainThreadInitializedObject<>(UserCache::new); - private final Context mContext; - private final UserManager mUserManager; - private final ArrayList<Runnable> mUserChangeListeners = new ArrayList<>(); + private final List<BiConsumer<UserHandle, String>> mUserEventListeners = new ArrayList<>(); private final SimpleBroadcastReceiver mUserChangeReceiver = new SimpleBroadcastReceiver(this::onUsersChanged); - private LongSparseArray<UserHandle> mUsers; - // Create a separate reverse map as LongSparseArray.indexOfValue checks if objects are same - // and not {@link Object#equals} - private ArrayMap<UserHandle, Long> mUserToSerialMap; + private final Context mContext; + + @NonNull + private Map<UserHandle, Long> mUserToSerialMap; private UserCache(Context context) { mContext = context; - mUserManager = context.getSystemService(UserManager.class); + mUserToSerialMap = Collections.emptyMap(); + MODEL_EXECUTOR.execute(this::initAsync); } - private void onUsersChanged(Intent intent) { - testLogD(WORK_TAB_MISSING, "onUsersChanged intent: " + intent); - enableAndResetCache(); - mUserChangeListeners.forEach(Runnable::run); + @Override + public void close() { + MODEL_EXECUTOR.execute(() -> mUserChangeReceiver.unregisterReceiverSafely(mContext)); } - /** - * Adds a listener for user additions and removals - */ - public SafeCloseable addUserChangeListener(Runnable command) { - synchronized (this) { - if (mUserChangeListeners.isEmpty()) { - // Enable caching and start listening for user broadcast - mUserChangeReceiver.register(mContext, - Intent.ACTION_MANAGED_PROFILE_ADDED, - Intent.ACTION_MANAGED_PROFILE_REMOVED); - enableAndResetCache(); - } - mUserChangeListeners.add(command); - return () -> removeUserChangeListener(command); - } + @WorkerThread + private void initAsync() { + mUserChangeReceiver.register(mContext, + Intent.ACTION_MANAGED_PROFILE_AVAILABLE, + Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, + ACTION_PROFILE_ADDED, + ACTION_PROFILE_REMOVED, + ACTION_PROFILE_UNLOCKED, + ACTION_PROFILE_LOCKED); + updateCache(); } - private void enableAndResetCache() { - synchronized (this) { - mUsers = new LongSparseArray<>(); - mUserToSerialMap = new ArrayMap<>(); - List<UserHandle> users = mUserManager.getUserProfiles(); - if (users != null) { - for (UserHandle user : users) { - testLogD(WORK_TAB_MISSING, "caching user: " + user); - long serial = mUserManager.getSerialNumberForUser(user); - mUsers.put(serial, user); - mUserToSerialMap.put(user, serial); - } - } + @AnyThread + private void onUsersChanged(Intent intent) { + MODEL_EXECUTOR.execute(this::updateCache); + UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); + if (user == null) { + return; } + String action = intent.getAction(); + mUserEventListeners.forEach(l -> l.accept(user, action)); } - private void removeUserChangeListener(Runnable command) { - synchronized (this) { - mUserChangeListeners.remove(command); - if (mUserChangeListeners.isEmpty()) { - // Disable cache and stop listening - mContext.unregisterReceiver(mUserChangeReceiver); + @WorkerThread + private void updateCache() { + mUserToSerialMap = queryAllUsers(mContext.getSystemService(UserManager.class)); + } - mUsers = null; - mUserToSerialMap = null; - } - } + /** + * Adds a listener for user additions and removals + */ + public SafeCloseable addUserEventListener(BiConsumer<UserHandle, String> listener) { + mUserEventListeners.add(listener); + return () -> mUserEventListeners.remove(listener); } /** * @see UserManager#getSerialNumberForUser(UserHandle) */ public long getSerialNumberForUser(UserHandle user) { - synchronized (this) { - if (mUserToSerialMap != null) { - Long serial = mUserToSerialMap.get(user); - return serial == null ? 0 : serial; - } - } - return mUserManager.getSerialNumberForUser(user); + Long serial = mUserToSerialMap.get(user); + return serial == null ? 0 : serial; } /** * @see UserManager#getUserForSerialNumber(long) */ public UserHandle getUserForSerialNumber(long serialNumber) { - synchronized (this) { - if (mUsers != null) { - return mUsers.get(serialNumber); - } - } - return mUserManager.getUserForSerialNumber(serialNumber); + Long value = serialNumber; + return mUserToSerialMap + .entrySet() + .stream() + .filter(entry -> value.equals(entry.getValue())) + .findFirst() + .map(Map.Entry::getKey) + .orElse(Process.myUserHandle()); } /** * @see UserManager#getUserProfiles() */ public List<UserHandle> getUserProfiles() { - StringBuilder usersToReturn = new StringBuilder(); - List<UserHandle> users; - if (sDebugTracing) { - users = mUserManager.getUserProfiles(); - for (UserHandle u : users) { - usersToReturn.append(u).append(" && "); - } - testLogD(WORK_TAB_MISSING, "users from userManager: " + usersToReturn); - } + return List.copyOf(mUserToSerialMap.keySet()); + } - synchronized (this) { - if (mUsers != null) { - usersToReturn = new StringBuilder(); - for (UserHandle u : mUserToSerialMap.keySet()) { - usersToReturn.append(u).append(" && "); - } - testLogD(WORK_TAB_MISSING, "users from cache: " + usersToReturn); - return new ArrayList<>(mUserToSerialMap.keySet()); - } else { - testLogD(WORK_TAB_MISSING, "users from cache null"); + private static Map<UserHandle, Long> queryAllUsers(UserManager userManager) { + Map<UserHandle, Long> users = new ArrayMap<>(); + List<UserHandle> usersActual = userManager.getUserProfiles(); + if (usersActual != null) { + for (UserHandle user : usersActual) { + long serial = userManager.getSerialNumberForUser(user); + users.put(user, serial); } } - - users = mUserManager.getUserProfiles(); - return users == null ? Collections.emptyList() : users; + return users; } } diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java index 72f99cb4df..e0f245fd27 100644 --- a/src/com/android/launcher3/popup/ArrowPopup.java +++ b/src/com/android/launcher3/popup/ArrowPopup.java @@ -18,11 +18,11 @@ package com.android.launcher3.popup; import static androidx.core.content.ContextCompat.getColorStateList; -import static com.android.launcher3.anim.Interpolators.ACCELERATED_EASE; -import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE; -import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.ACCELERATED_EASE; +import static com.android.app.animation.Interpolators.DECELERATED_EASE; +import static com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE; +import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.config.FeatureFlags.ENABLE_MATERIAL_U_POPUP; import android.animation.Animator; @@ -673,10 +673,6 @@ public abstract class ArrowPopup<T extends Context & ActivityContext> } mIsOpen = false; - mOpenCloseAnimator = getOpenCloseAnimator(false, mCloseDuration, mCloseFadeStartDelay, - mCloseFadeDuration, mCloseChildFadeStartDelay, mCloseChildFadeDuration, - ACCELERATED_EASE); - mOpenCloseAnimator = ENABLE_MATERIAL_U_POPUP.get() ? getMaterialUOpenCloseAnimator( false, diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java index 7c9ab87ebc..eab096974f 100644 --- a/src/com/android/launcher3/popup/RemoteActionShortcut.java +++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java @@ -16,10 +16,12 @@ package com.android.launcher3.popup; +import static com.android.launcher3.Utilities.allowBGLaunch; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.annotation.TargetApi; +import android.app.ActivityOptions; import android.app.PendingIntent; import android.app.RemoteAction; import android.content.Context; @@ -84,6 +86,8 @@ public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> { final WeakReference<BaseDraggingActivity> weakTarget = new WeakReference<>(mTarget); final String actionIdentity = mAction.getTitle() + ", " + mItemInfo.getTargetComponent().getPackageName(); + + ActivityOptions options = allowBGLaunch(ActivityOptions.makeBasic()); try { if (DEBUG) Log.d(TAG, "Sending action: " + actionIdentity); mAction.getActionIntent().send( @@ -103,7 +107,9 @@ public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> { } } }, - MAIN_EXECUTOR.getHandler()); + MAIN_EXECUTOR.getHandler(), + null, + options.toBundle()); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Remote action canceled: " + actionIdentity, e); Toast.makeText(mTarget, mTarget.getString( diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java index c718dcc877..575551b9f2 100644 --- a/src/com/android/launcher3/provider/LauncherDbUtils.java +++ b/src/com/android/launcher3/provider/LauncherDbUtils.java @@ -28,7 +28,6 @@ import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.Icon; -import android.os.Binder; import android.os.PersistableBundle; import android.os.Process; import android.os.UserManager; @@ -50,6 +49,13 @@ import com.android.launcher3.util.IntSet; */ public class LauncherDbUtils { + /** + * Returns a string which can be used as a where clause for DB query to match the given itemId + */ + public static String itemIdMatch(int itemId) { + return "_id=" + itemId; + } + public static IntArray queryIntArray(boolean distinct, SQLiteDatabase db, String tableName, String columnName, String selection, String groupBy, String orderBy) { IntArray out = new IntArray(); @@ -114,6 +120,10 @@ public class LauncherDbUtils { deletedShortcuts.add(lc.id); continue; } + if (TextUtils.isEmpty(lc.getTitle())) { + deletedShortcuts.add(lc.id); + continue; + } // Make sure the target intent can be launched without any permissions. Otherwise remove // the shortcut @@ -166,7 +176,7 @@ public class LauncherDbUtils { /** * Utility class to simplify managing sqlite transactions */ - public static class SQLiteTransaction extends Binder implements AutoCloseable { + public static class SQLiteTransaction implements AutoCloseable { private final SQLiteDatabase mDb; public SQLiteTransaction(SQLiteDatabase db) { diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java index c554def0de..4725dd16b2 100644 --- a/src/com/android/launcher3/provider/RestoreDbTask.java +++ b/src/com/android/launcher3/provider/RestoreDbTask.java @@ -82,11 +82,15 @@ public class RestoreDbTask { public static final String APPWIDGET_OLD_IDS = "appwidget_old_ids"; public static final String APPWIDGET_IDS = "appwidget_ids"; + private static final String[] DB_COLUMNS_TO_LOG = {"profileId", "title", "itemType", "screen", + "container", "cellX", "cellY", "spanX", "spanY", "intent"}; + /** * Tries to restore the backup DB if needed */ public static void restoreIfNeeded(Context context, ModelDbController dbController) { if (!isPending(context)) { + Log.d(TAG, "No restore task pending, exiting RestoreDbTask"); return; } if (!performRestore(context, dbController)) { @@ -106,6 +110,7 @@ public class RestoreDbTask { private static boolean performRestore(Context context, ModelDbController controller) { SQLiteDatabase db = controller.getDb(); + FileLog.d(TAG, "performRestore: starting restore from db"); try (SQLiteTransaction t = new SQLiteTransaction(db)) { RestoreDbTask task = new RestoreDbTask(); task.sanitizeDB(context, controller, db, new BackupManager(context)); @@ -133,10 +138,11 @@ public class RestoreDbTask { @VisibleForTesting protected int sanitizeDB(Context context, ModelDbController controller, SQLiteDatabase db, BackupManager backupManager) throws Exception { + FileLog.d(TAG, "Old Launcher Database before sanitizing:"); // Primary user ids long myProfileId = controller.getSerialNumberForUser(myUserHandle()); long oldProfileId = getDefaultProfileId(db); - Log.d(TAG, "sanitizeDB: myProfileId=" + myProfileId + " oldProfileId=" + oldProfileId); + FileLog.d(TAG, "sanitizeDB: myProfileId=" + myProfileId + " oldProfileId=" + oldProfileId); LongSparseArray<Long> oldManagedProfileIds = getManagedProfileIds(db, oldProfileId); LongSparseArray<Long> profileMapping = new LongSparseArray<>(oldManagedProfileIds.size() + 1); @@ -149,8 +155,11 @@ public class RestoreDbTask { if (user != null) { long newManagedProfileId = controller.getSerialNumberForUser(user); profileMapping.put(oldManagedProfileId, newManagedProfileId); - Log.d(TAG, "sanitizeDB: managed profile id=" + oldManagedProfileId + FileLog.d(TAG, "sanitizeDB: managed profile id=" + oldManagedProfileId + " should be mapped to new id=" + newManagedProfileId); + } else { + FileLog.e(TAG, "sanitizeDB: No User found for old profileId, Ancestral Serial " + + "Number: " + oldManagedProfileId); } } @@ -161,11 +170,13 @@ public class RestoreDbTask { for (int i = numProfiles - 1; i >= 1; --i) { profileIds[i] = Long.toString(profileMapping.keyAt(i)); } + final String[] args = new String[profileIds.length]; Arrays.fill(args, "?"); final String where = "profileId NOT IN (" + TextUtils.join(", ", Arrays.asList(args)) + ")"; - int itemsDeleted = db.delete(Favorites.TABLE_NAME, where, profileIds); - FileLog.d(TAG, itemsDeleted + " items from unrestored user(s) were deleted"); + logUnrestoredItems(db, where, profileIds); + int itemsDeletedCount = db.delete(Favorites.TABLE_NAME, where, profileIds); + FileLog.d(TAG, itemsDeletedCount + " total items from unrestored user(s) were deleted"); // Mark all items as restored. boolean keepAllIcons = Utilities.isPropertyEnabled(LogConfig.KEEP_ALL_ICONS); @@ -219,8 +230,48 @@ public class RestoreDbTask { // Override shortcuts maybeOverrideShortcuts(context, controller, db, myProfileId); + return itemsDeletedCount; + } - return itemsDeleted; + /** + * Queries and logs the items we will delete from unrestored profiles in the launcher db. + * This is to understand why items might be missing during the restore process for Launcher. + * @param database the Launcher db to query from. + * @param where the SELECT statement to query items that will be deleted. + * @param profileIds the profile ID's the user will be migrating to. + */ + private void logUnrestoredItems(SQLiteDatabase database, String where, String[] profileIds) { + try (Cursor itemsToDelete = database.query( + /* table */ Favorites.TABLE_NAME, + /* columns */ DB_COLUMNS_TO_LOG, + /* selection */ where, + /* selection args */ profileIds, + /* groupBy */ null, + /* having */ null, + /* orderBy */ null + )) { + if (itemsToDelete.moveToFirst()) { + String[] columnNames = itemsToDelete.getColumnNames(); + StringBuilder stringBuilder = new StringBuilder( + "items to be deleted from the Favorites Table during restore:\n" + ); + do { + for (String columnName : columnNames) { + stringBuilder.append(columnName) + .append("=") + .append(itemsToDelete.getString( + itemsToDelete.getColumnIndex(columnName))) + .append(" "); + } + stringBuilder.append("\n"); + } while (itemsToDelete.moveToNext()); + FileLog.d(TAG, stringBuilder.toString()); + } else { + FileLog.d(TAG, "logDeletedItems: No items found to delete"); + } + } catch (Exception e) { + FileLog.e(TAG, "logDeletedItems: Error reading from database", e); + } } /** @@ -321,7 +372,7 @@ public class RestoreDbTask { * Marks the DB state as pending restoration */ public static void setPending(Context context) { - FileLog.d(TAG, "Restore data received through full backup "); + FileLog.d(TAG, "Restore data received through full backup"); LauncherPrefs.get(context) .putSync(RESTORE_DEVICE.to(new DeviceGridState(context).getDeviceType())); } diff --git a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt new file mode 100644 index 0000000000..3c59c1d63f --- /dev/null +++ b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt @@ -0,0 +1,103 @@ +/* + * 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.launcher3.recyclerview + +import android.content.Context +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.RecycledViewPool +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.android.launcher3.BubbleTextView +import com.android.launcher3.allapps.BaseAllAppsAdapter +import com.android.launcher3.config.FeatureFlags +import com.android.launcher3.util.Executors.MAIN_EXECUTOR +import com.android.launcher3.util.Executors.VIEW_PREINFLATION_EXECUTOR +import com.android.launcher3.views.ActivityContext +import java.util.concurrent.Future + +const val PREINFLATE_ICONS_ROW_COUNT = 4 +const val EXTRA_ICONS_COUNT = 2 + +/** + * An [RecycledViewPool] that preinflates app icons ([ViewHolder] of [BubbleTextView]) of all apps + * [RecyclerView]. The view inflation will happen on background thread and inflated [ViewHolder]s + * will be added to [RecycledViewPool] on main thread. + */ +class AllAppsRecyclerViewPool<T> : RecycledViewPool() { + + private var future: Future<Void>? = null + + /** + * Preinflate app icons. If all apps RV cannot be scrolled down, we don't need to preinflate. + */ + fun <T> preInflateAllAppsViewHolders(context: T) where T : Context, T : ActivityContext { + val appsView = context.appsView ?: return + val activeRv: RecyclerView = appsView.activeRecyclerView ?: return + val preInflateCount = getPreinflateCount(context) + if (preInflateCount <= 0) { + return + } + + // Because we perform onCreateViewHolder() on worker thread, we need a separate + // adapter/inflator object as they are not thread-safe. Note that the adapter + // just need to perform onCreateViewHolder(parent, VIEW_TYPE_ICON) so it doesn't need + // data source information. + val adapter: RecyclerView.Adapter<BaseAllAppsAdapter.ViewHolder> = + object : BaseAllAppsAdapter<T>(context, context.appsView.layoutInflater, null, null) { + override fun setAppsPerRow(appsPerRow: Int) = Unit + override fun getLayoutManager(): RecyclerView.LayoutManager? = null + } + + // Inflate view holders on background thread, and added to view pool on main thread. + future?.cancel(true) + future = + VIEW_PREINFLATION_EXECUTOR.submit<Void> { + val viewHolders = + Array(preInflateCount) { + adapter.createViewHolder(activeRv, BaseAllAppsAdapter.VIEW_TYPE_ICON) + } + MAIN_EXECUTOR.execute { + for (i in 0 until minOf(viewHolders.size, getPreinflateCount(context))) { + putRecycledView(viewHolders[i]) + } + } + null + } + } + + /** + * After testing on phone, foldable and tablet, we found [PREINFLATE_ICONS_ROW_COUNT] rows of + * app icons plus [EXTRA_ICONS_COUNT] is the magic minimal count of app icons to preinflate to + * suffice fast scrolling. + * + * Note that if [FeatureFlags.ALL_APPS_GONE_VISIBILITY] is enabled, we need to preinfate extra + * app icons in size of one all apps pages, so that opening all apps don't need to inflate app + * icons. + */ + fun <T> getPreinflateCount(context: T): Int where T : Context, T : ActivityContext { + var targetPreinflateCount = + PREINFLATE_ICONS_ROW_COUNT * context.deviceProfile.numShownAllAppsColumns + + EXTRA_ICONS_COUNT + if (FeatureFlags.ALL_APPS_GONE_VISIBILITY.get()) { + val grid = ActivityContext.lookupContext<T>(context).deviceProfile + val approxRows = + Math.ceil((grid.availableHeightPx / grid.allAppsIconSizePx).toDouble()).toInt() + targetPreinflateCount += (approxRows + 1) * grid.numShownAllAppsColumns + } + val existingPreinflateCount = getRecycledViewCount(BaseAllAppsAdapter.VIEW_TYPE_ICON) + return targetPreinflateCount - existingPreinflateCount + } +} diff --git a/src/com/android/launcher3/responsive/AllAppsSpecs.kt b/src/com/android/launcher3/responsive/AllAppsSpecs.kt new file mode 100644 index 0000000000..8ed3ffc1bf --- /dev/null +++ b/src/com/android/launcher3/responsive/AllAppsSpecs.kt @@ -0,0 +1,104 @@ +/* + * 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.launcher3.responsive + +import android.content.res.TypedArray +import com.android.launcher3.R +import com.android.launcher3.responsive.ResponsiveSpec.SpecType +import com.android.launcher3.util.ResourceHelper + +class AllAppsSpecs(widthSpecs: List<AllAppsSpec>, heightSpecs: List<AllAppsSpec>) : + ResponsiveSpecs<AllAppsSpec>(widthSpecs, heightSpecs) { + + fun getCalculatedWidthSpec( + columns: Int, + availableWidth: Int, + calculatedWorkspaceSpec: CalculatedWorkspaceSpec + ): CalculatedAllAppsSpec { + check(calculatedWorkspaceSpec.spec.specType == SpecType.WIDTH) { + "Invalid specType for CalculatedWorkspaceSpec. " + + "Expected: ${SpecType.WIDTH} - " + + "Found: ${calculatedWorkspaceSpec.spec.specType}}" + } + + val spec = getWidthSpec(availableWidth) + return CalculatedAllAppsSpec(availableWidth, columns, spec, calculatedWorkspaceSpec) + } + + fun getCalculatedHeightSpec( + rows: Int, + availableHeight: Int, + calculatedWorkspaceSpec: CalculatedWorkspaceSpec + ): CalculatedAllAppsSpec { + check(calculatedWorkspaceSpec.spec.specType == SpecType.HEIGHT) { + "Invalid specType for CalculatedWorkspaceSpec. " + + "Expected: ${SpecType.HEIGHT} - " + + "Found: ${calculatedWorkspaceSpec.spec.specType}}" + } + + val spec = getHeightSpec(availableHeight) + return CalculatedAllAppsSpec(availableHeight, rows, spec, calculatedWorkspaceSpec) + } + + companion object { + private const val XML_ALL_APPS_SPEC = "allAppsSpec" + + @JvmStatic + fun create(resourceHelper: ResourceHelper): AllAppsSpecs { + val parser = ResponsiveSpecsParser(resourceHelper) + val specs = parser.parseXML(XML_ALL_APPS_SPEC, ::AllAppsSpec) + val (widthSpecs, heightSpecs) = specs.partition { it.specType == SpecType.WIDTH } + return AllAppsSpecs(widthSpecs, heightSpecs) + } + } +} + +data class AllAppsSpec( + override val maxAvailableSize: Int, + override val specType: SpecType, + override val startPadding: SizeSpec, + override val endPadding: SizeSpec, + override val gutter: SizeSpec, + override val cellSize: SizeSpec +) : ResponsiveSpec(maxAvailableSize, specType, startPadding, endPadding, gutter, cellSize) { + + init { + check(isValid()) { "Invalid AllAppsSpec found." } + } + + constructor( + attrs: TypedArray, + specs: Map<String, SizeSpec> + ) : this( + maxAvailableSize = + attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0), + specType = + SpecType.values()[ + attrs.getInt(R.styleable.ResponsiveSpec_specType, SpecType.HEIGHT.ordinal)], + startPadding = specs.getOrError(SizeSpec.XmlTags.START_PADDING), + endPadding = specs.getOrError(SizeSpec.XmlTags.END_PADDING), + gutter = specs.getOrError(SizeSpec.XmlTags.GUTTER), + cellSize = specs.getOrError(SizeSpec.XmlTags.CELL_SIZE) + ) +} + +class CalculatedAllAppsSpec( + availableSpace: Int, + cells: Int, + spec: AllAppsSpec, + calculatedWorkspaceSpec: CalculatedWorkspaceSpec +) : CalculatedResponsiveSpec(availableSpace, cells, spec, calculatedWorkspaceSpec) diff --git a/src/com/android/launcher3/responsive/FolderSpecs.kt b/src/com/android/launcher3/responsive/FolderSpecs.kt new file mode 100644 index 0000000000..bc2db28ef1 --- /dev/null +++ b/src/com/android/launcher3/responsive/FolderSpecs.kt @@ -0,0 +1,105 @@ +/* + * 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.launcher3.responsive + +import android.content.res.TypedArray +import com.android.launcher3.R +import com.android.launcher3.responsive.ResponsiveSpec.SpecType +import com.android.launcher3.util.ResourceHelper + +class FolderSpecs(widthSpecs: List<FolderSpec>, heightSpecs: List<FolderSpec>) : + ResponsiveSpecs<FolderSpec>(widthSpecs, heightSpecs) { + + fun getCalculatedWidthSpec( + columns: Int, + availableWidth: Int, + calculatedWorkspaceSpec: CalculatedWorkspaceSpec + ): CalculatedFolderSpec { + check(calculatedWorkspaceSpec.spec.specType == SpecType.WIDTH) { + "Invalid specType for CalculatedWorkspaceSpec. " + + "Expected: ${SpecType.WIDTH} - " + + "Found: ${calculatedWorkspaceSpec.spec.specType}}" + } + + val spec = getWidthSpec(availableWidth) + return CalculatedFolderSpec(availableWidth, columns, spec, calculatedWorkspaceSpec) + } + + fun getCalculatedHeightSpec( + rows: Int, + availableHeight: Int, + calculatedWorkspaceSpec: CalculatedWorkspaceSpec + ): CalculatedFolderSpec { + check(calculatedWorkspaceSpec.spec.specType == SpecType.HEIGHT) { + "Invalid specType for CalculatedWorkspaceSpec. " + + "Expected: ${SpecType.HEIGHT} - " + + "Found: ${calculatedWorkspaceSpec.spec.specType}}" + } + + val spec = getHeightSpec(availableHeight) + return CalculatedFolderSpec(availableHeight, rows, spec, calculatedWorkspaceSpec) + } + + companion object { + + private const val XML_FOLDER_SPEC = "folderSpec" + + @JvmStatic + fun create(resourceHelper: ResourceHelper): FolderSpecs { + val parser = ResponsiveSpecsParser(resourceHelper) + val specs = parser.parseXML(XML_FOLDER_SPEC, ::FolderSpec) + val (widthSpecs, heightSpecs) = specs.partition { it.specType == SpecType.WIDTH } + return FolderSpecs(widthSpecs, heightSpecs) + } + } +} + +data class FolderSpec( + override val maxAvailableSize: Int, + override val specType: SpecType, + override val startPadding: SizeSpec, + override val endPadding: SizeSpec, + override val gutter: SizeSpec, + override val cellSize: SizeSpec +) : ResponsiveSpec(maxAvailableSize, specType, startPadding, endPadding, gutter, cellSize) { + + init { + check(isValid()) { "Invalid FolderSpec found." } + } + + constructor( + attrs: TypedArray, + specs: Map<String, SizeSpec> + ) : this( + maxAvailableSize = + attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0), + specType = + SpecType.values()[ + attrs.getInt(R.styleable.ResponsiveSpec_specType, SpecType.HEIGHT.ordinal)], + startPadding = specs.getOrError(SizeSpec.XmlTags.START_PADDING), + endPadding = specs.getOrError(SizeSpec.XmlTags.END_PADDING), + gutter = specs.getOrError(SizeSpec.XmlTags.GUTTER), + cellSize = specs.getOrError(SizeSpec.XmlTags.CELL_SIZE) + ) +} + +class CalculatedFolderSpec( + availableSpace: Int, + cells: Int, + spec: FolderSpec, + calculatedWorkspaceSpec: CalculatedWorkspaceSpec +) : CalculatedResponsiveSpec(availableSpace, cells, spec, calculatedWorkspaceSpec) diff --git a/src/com/android/launcher3/responsive/HotseatSpecs.kt b/src/com/android/launcher3/responsive/HotseatSpecs.kt new file mode 100644 index 0000000000..482508d085 --- /dev/null +++ b/src/com/android/launcher3/responsive/HotseatSpecs.kt @@ -0,0 +1,122 @@ +/* + * 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.launcher3.responsive + +import android.content.res.TypedArray +import android.util.Log +import com.android.launcher3.R +import com.android.launcher3.util.ResourceHelper + +class HotseatSpecs(val specs: List<HotseatSpec>) { + + fun getCalculatedHeightSpec(availableHeight: Int): CalculatedHotseatSpec { + val spec = specs.firstOrNull { availableHeight <= it.maxAvailableSize } + check(spec != null) { "No available height spec found within $availableHeight." } + return CalculatedHotseatSpec(availableHeight, spec) + } + + companion object { + private const val XML_HOTSEAT_SPEC = "hotseatSpec" + + @JvmStatic + fun create(resourceHelper: ResourceHelper): HotseatSpecs { + val parser = ResponsiveSpecsParser(resourceHelper) + val specs = parser.parseXML(XML_HOTSEAT_SPEC, ::HotseatSpec) + return HotseatSpecs(specs.filter { it.specType == ResponsiveSpec.SpecType.HEIGHT }) + } + } +} + +data class HotseatSpec( + val maxAvailableSize: Int, + val specType: ResponsiveSpec.SpecType, + val hotseatQsbSpace: SizeSpec +) { + + init { + check(isValid()) { "Invalid HotseatSpec found." } + } + + constructor( + attrs: TypedArray, + specs: Map<String, SizeSpec> + ) : this( + maxAvailableSize = + attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0), + specType = + ResponsiveSpec.SpecType.values()[ + attrs.getInt( + R.styleable.ResponsiveSpec_specType, + ResponsiveSpec.SpecType.HEIGHT.ordinal + )], + hotseatQsbSpace = specs.getOrError(SizeSpec.XmlTags.HOTSEAT_QSB_SPACE) + ) + + fun isValid(): Boolean { + if (maxAvailableSize <= 0) { + Log.e(LOG_TAG, "${this::class.simpleName}#isValid - maxAvailableSize <= 0") + return false + } + + // All specs need to be individually valid + if (!allSpecsAreValid()) { + Log.e(LOG_TAG, "${this::class.simpleName}#isValid - !allSpecsAreValid()") + return false + } + + return true + } + + private fun allSpecsAreValid(): Boolean { + return hotseatQsbSpace.isValid() && hotseatQsbSpace.onlyFixedSize() + } + + companion object { + private const val LOG_TAG = "HotseatSpec" + } +} + +class CalculatedHotseatSpec(val availableSpace: Int, val spec: HotseatSpec) { + + var hotseatQsbSpace: Int = 0 + private set + + init { + hotseatQsbSpace = spec.hotseatQsbSpace.getCalculatedValue(availableSpace) + } + + override fun hashCode(): Int { + var result = availableSpace.hashCode() + result = 31 * result + hotseatQsbSpace.hashCode() + result = 31 * result + spec.hashCode() + return result + } + + override fun equals(other: Any?): Boolean { + return other is CalculatedHotseatSpec && + availableSpace == other.availableSpace && + hotseatQsbSpace == other.hotseatQsbSpace && + spec == other.spec + } + + override fun toString(): String { + return "${this::class.simpleName}(" + + "availableSpace=$availableSpace, hotseatQsbSpace=$hotseatQsbSpace, " + + "${spec::class.simpleName}.maxAvailableSize=${spec.maxAvailableSize}" + + ")" + } +} diff --git a/src/com/android/launcher3/responsive/ResponsiveSpecs.kt b/src/com/android/launcher3/responsive/ResponsiveSpecs.kt new file mode 100644 index 0000000000..72a0ea4f72 --- /dev/null +++ b/src/com/android/launcher3/responsive/ResponsiveSpecs.kt @@ -0,0 +1,222 @@ +/* + * 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.launcher3.responsive + +import android.util.Log + +/** + * Base class for responsive specs that holds a list of width and height specs. + * + * @param widthSpecs List of width responsive specifications + * @param heightSpecs List of height responsive specifications + */ +abstract class ResponsiveSpecs<T : ResponsiveSpec>( + val widthSpecs: List<T>, + val heightSpecs: List<T> +) { + + init { + check(widthSpecs.isNotEmpty() && heightSpecs.isNotEmpty()) { + "${this::class.simpleName} is incomplete - " + + "width list size = ${widthSpecs.size}; " + + "height list size = ${heightSpecs.size}." + } + } + + /** + * Get a [ResponsiveSpec] for width within the breakpoint. + * + * @param availableWidth The width breakpoint for the spec + * @return A [ResponsiveSpec] for width. + */ + fun getWidthSpec(availableWidth: Int): T { + val spec = widthSpecs.firstOrNull { availableWidth <= it.maxAvailableSize } + check(spec != null) { "No available width spec found within $availableWidth." } + return spec + } + + /** + * Get a [ResponsiveSpec] for height within the breakpoint. + * + * @param availableHeight The height breakpoint for the spec + * @return A [ResponsiveSpec] for height. + */ + fun getHeightSpec(availableHeight: Int): T { + val spec = heightSpecs.firstOrNull { availableHeight <= it.maxAvailableSize } + check(spec != null) { "No available height spec found within $availableHeight." } + return spec + } +} + +/** + * Base class for a responsive specification that is used to calculate the paddings, gutter and cell + * size. + * + * @param maxAvailableSize indicates the breakpoint to use this specification. + * @param specType indicates whether the paddings and gutters will be applied vertically or + * horizontally. + * @param startPadding padding used at the top or left (right in RTL) in the workspace folder. + * @param endPadding padding used at the bottom or right (left in RTL) in the workspace folder. + * @param gutter the space between the cells vertically or horizontally depending on the [specType]. + * @param cellSize height or width of the cell depending on the [specType]. + */ +abstract class ResponsiveSpec( + open val maxAvailableSize: Int, + open val specType: SpecType, + open val startPadding: SizeSpec, + open val endPadding: SizeSpec, + open val gutter: SizeSpec, + open val cellSize: SizeSpec +) { + open fun isValid(): Boolean { + if (maxAvailableSize <= 0) { + Log.e(LOG_TAG, "${this::class.simpleName}#isValid - maxAvailableSize <= 0") + return false + } + + // All specs need to be individually valid + if (!allSpecsAreValid()) { + Log.e(LOG_TAG, "${this::class.simpleName}#isValid - !allSpecsAreValid()") + return false + } + + return true + } + + private fun allSpecsAreValid(): Boolean { + return startPadding.isValid() && + endPadding.isValid() && + gutter.isValid() && + cellSize.isValid() + } + + enum class SpecType { + HEIGHT, + WIDTH + } + + companion object { + private const val LOG_TAG = "ResponsiveSpec" + } +} + +/** + * Calculated responsive specs contains the final paddings, gutter and cell size in pixels after + * they are calculated from the available space, cells and workspace specs. + */ +sealed class CalculatedResponsiveSpec { + var availableSpace: Int = 0 + private set + + var cells: Int = 0 + private set + + var startPaddingPx: Int = 0 + private set + + var endPaddingPx: Int = 0 + private set + + var gutterPx: Int = 0 + private set + + var cellSizePx: Int = 0 + private set + + var spec: ResponsiveSpec + private set + + constructor( + availableSpace: Int, + cells: Int, + spec: ResponsiveSpec, + calculatedWorkspaceSpec: CalculatedWorkspaceSpec + ) { + this.availableSpace = availableSpace + this.cells = cells + this.spec = spec + + // Map if is fixedSize, ofAvailableSpace or matchWorkspace + startPaddingPx = + spec.startPadding.getCalculatedValue( + availableSpace, + calculatedWorkspaceSpec.startPaddingPx + ) + endPaddingPx = + spec.endPadding.getCalculatedValue(availableSpace, calculatedWorkspaceSpec.endPaddingPx) + gutterPx = spec.gutter.getCalculatedValue(availableSpace, calculatedWorkspaceSpec.gutterPx) + cellSizePx = + spec.cellSize.getCalculatedValue(availableSpace, calculatedWorkspaceSpec.cellSizePx) + + updateRemainderSpaces(availableSpace, cells, spec) + } + + constructor(availableSpace: Int, cells: Int, spec: ResponsiveSpec) { + this.availableSpace = availableSpace + this.cells = cells + this.spec = spec + + // Map if is fixedSize or ofAvailableSpace + startPaddingPx = spec.startPadding.getCalculatedValue(availableSpace) + endPaddingPx = spec.endPadding.getCalculatedValue(availableSpace) + gutterPx = spec.gutter.getCalculatedValue(availableSpace) + cellSizePx = spec.cellSize.getCalculatedValue(availableSpace) + + updateRemainderSpaces(availableSpace, cells, spec) + } + + private fun updateRemainderSpaces(availableSpace: Int, cells: Int, spec: ResponsiveSpec) { + val gutters = cells - 1 + val usedSpace = startPaddingPx + endPaddingPx + (gutterPx * gutters) + (cellSizePx * cells) + val remainderSpace = availableSpace - usedSpace + + startPaddingPx = spec.startPadding.getRemainderSpaceValue(remainderSpace, startPaddingPx) + endPaddingPx = spec.endPadding.getRemainderSpaceValue(remainderSpace, endPaddingPx) + gutterPx = spec.gutter.getRemainderSpaceValue(remainderSpace, gutterPx) + cellSizePx = spec.cellSize.getRemainderSpaceValue(remainderSpace, cellSizePx) + } + + override fun hashCode(): Int { + var result = availableSpace.hashCode() + result = 31 * result + cells.hashCode() + result = 31 * result + startPaddingPx.hashCode() + result = 31 * result + endPaddingPx.hashCode() + result = 31 * result + gutterPx.hashCode() + result = 31 * result + cellSizePx.hashCode() + result = 31 * result + spec.hashCode() + return result + } + + override fun equals(other: Any?): Boolean { + return other is CalculatedResponsiveSpec && + availableSpace == other.availableSpace && + cells == other.cells && + startPaddingPx == other.startPaddingPx && + endPaddingPx == other.endPaddingPx && + gutterPx == other.gutterPx && + cellSizePx == other.cellSizePx && + spec == other.spec + } + + override fun toString(): String { + return "${this::class.simpleName}(" + + "availableSpace=$availableSpace, cells=$cells, startPaddingPx=$startPaddingPx, " + + "endPaddingPx=$endPaddingPx, gutterPx=$gutterPx, cellSizePx=$cellSizePx, " + + "${spec::class.simpleName}.maxAvailableSize=${spec.maxAvailableSize}" + + ")" + } +} diff --git a/src/com/android/launcher3/responsive/ResponsiveSpecsParser.kt b/src/com/android/launcher3/responsive/ResponsiveSpecsParser.kt new file mode 100644 index 0000000000..a89b619c00 --- /dev/null +++ b/src/com/android/launcher3/responsive/ResponsiveSpecsParser.kt @@ -0,0 +1,90 @@ +/* + * 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.launcher3.responsive + +import android.content.res.TypedArray +import android.content.res.XmlResourceParser +import android.util.Xml +import com.android.launcher3.R +import com.android.launcher3.util.ResourceHelper +import java.io.IOException +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException + +class ResponsiveSpecsParser(private val resourceHelper: ResourceHelper) { + + private fun parseSizeSpecs(parser: XmlResourceParser): Map<String, SizeSpec> { + val parentName = parser.name + parser.next() + + val result = mutableMapOf<String, SizeSpec>() + while (parser.eventType != XmlPullParser.END_DOCUMENT && parser.name != parentName) { + if (parser.eventType == XmlResourceParser.START_TAG) { + result[parser.name] = SizeSpec.create(resourceHelper, Xml.asAttributeSet(parser)) + } + parser.next() + } + + return result + } + + fun <T> parseXML( + tagName: String, + map: (attributes: TypedArray, sizeSpecs: Map<String, SizeSpec>) -> T + ): List<T> { + val parser: XmlResourceParser = resourceHelper.getXml() + + try { + val list = mutableListOf<T>() + + var eventType = parser.eventType + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlResourceParser.START_TAG && parser.name == tagName) { + val attrs = + resourceHelper.obtainStyledAttributes( + Xml.asAttributeSet(parser), + R.styleable.ResponsiveSpec + ) + + val sizeSpecs = parseSizeSpecs(parser) + list += map(attrs, sizeSpecs) + attrs.recycle() + } + + eventType = parser.next() + } + + parser.close() + + return list + } catch (e: Exception) { + when (e) { + is NoSuchFieldException, + is IOException, + is XmlPullParserException -> + throw RuntimeException("Failure parsing specs file.", e) + else -> throw e + } + } finally { + parser.close() + } + } +} + +fun Map<String, SizeSpec>.getOrError(key: String): SizeSpec { + return this.getOrElse(key) { error("Attr '$key' must be defined.") } +} diff --git a/src/com/android/launcher3/responsive/SizeSpec.kt b/src/com/android/launcher3/responsive/SizeSpec.kt new file mode 100644 index 0000000000..c868c9f7be --- /dev/null +++ b/src/com/android/launcher3/responsive/SizeSpec.kt @@ -0,0 +1,152 @@ +/* + * 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.launcher3.responsive + +import android.content.res.TypedArray +import android.util.AttributeSet +import android.util.Log +import android.util.TypedValue +import com.android.launcher3.R +import com.android.launcher3.util.ResourceHelper +import kotlin.math.roundToInt + +/** + * [SizeSpec] is an attribute used to represent a property in the responsive grid specs. + * + * @param fixedSize a fixed size in dp to be used + * @param ofAvailableSpace a percentage of the available space + * @param ofRemainderSpace a percentage of the remaining space (available space minus used space) + * @param matchWorkspace indicates whether the workspace value will be used or not. + * @param maxSize restricts the maximum value allowed for the [SizeSpec]. + */ +data class SizeSpec( + val fixedSize: Float = 0f, + val ofAvailableSpace: Float = 0f, + val ofRemainderSpace: Float = 0f, + val matchWorkspace: Boolean = false, + val maxSize: Int = Int.MAX_VALUE +) { + + /** Retrieves the correct value for [SizeSpec]. */ + fun getCalculatedValue(availableSpace: Int, workspaceValue: Int = 0): Int { + val calculatedValue = + when { + fixedSize > 0 -> fixedSize.roundToInt() + matchWorkspace -> workspaceValue + ofAvailableSpace > 0 -> (ofAvailableSpace * availableSpace).roundToInt() + else -> 0 + } + + return calculatedValue.coerceAtMost(maxSize) + } + + /** + * Calculates the [SizeSpec] value when remainder space value is defined. If no remainderSpace + * is 0, returns a default value. + */ + fun getRemainderSpaceValue(remainderSpace: Int, defaultValue: Int): Int { + val remainderSpaceValue = + if (ofRemainderSpace > 0) { + (ofRemainderSpace * remainderSpace).roundToInt() + } else { + defaultValue + } + + return remainderSpaceValue.coerceAtMost(maxSize) + } + + fun isValid(): Boolean { + // All attributes are empty + if (fixedSize < 0f && ofAvailableSpace <= 0f && ofRemainderSpace <= 0f && !matchWorkspace) { + Log.e(TAG, "SizeSpec#isValid - all attributes are empty") + return false + } + + // More than one attribute is filled + val attrCount = + (if (fixedSize > 0) 1 else 0) + + (if (ofAvailableSpace > 0) 1 else 0) + + (if (ofRemainderSpace > 0) 1 else 0) + + (if (matchWorkspace) 1 else 0) + if (attrCount > 1) { + Log.e(TAG, "SizeSpec#isValid - more than one attribute is filled") + return false + } + + // Values should be between 0 and 1 + if (ofAvailableSpace !in 0f..1f || ofRemainderSpace !in 0f..1f) { + Log.e(TAG, "SizeSpec#isValid - values should be between 0 and 1") + return false + } + + // Invalid fixed or max size + if (fixedSize < 0f || maxSize < 0f) { + Log.e(TAG, "SizeSpec#isValid - values should be bigger or equal to zero.") + return false + } + + if (fixedSize > 0f && fixedSize > maxSize) { + Log.e(TAG, "SizeSpec#isValid - fixed size should be smaller than the max size.") + return false + } + + return true + } + + fun onlyFixedSize(): Boolean { + if (ofAvailableSpace > 0 || ofRemainderSpace > 0 || matchWorkspace) { + Log.e(TAG, "SizeSpec#onlyFixedSize - only fixed size allowed for this tag") + return false + } + return true + } + + object XmlTags { + const val START_PADDING = "startPadding" + const val END_PADDING = "endPadding" + const val GUTTER = "gutter" + const val CELL_SIZE = "cellSize" + const val HOTSEAT_QSB_SPACE = "hotseatQsbSpace" + } + + companion object { + private const val TAG = "SizeSpec" + + private fun getValue(a: TypedArray, index: Int): Float { + return when (a.getType(index)) { + TypedValue.TYPE_DIMENSION -> a.getDimensionPixelSize(index, 0).toFloat() + TypedValue.TYPE_FLOAT -> a.getFloat(index, 0f) + else -> 0f + } + } + + fun create(resourceHelper: ResourceHelper, attrs: AttributeSet): SizeSpec { + val styledAttrs = resourceHelper.obtainStyledAttributes(attrs, R.styleable.SizeSpec) + + val fixedSize = getValue(styledAttrs, R.styleable.SizeSpec_fixedSize) + val ofAvailableSpace = getValue(styledAttrs, R.styleable.SizeSpec_ofAvailableSpace) + val ofRemainderSpace = getValue(styledAttrs, R.styleable.SizeSpec_ofRemainderSpace) + val matchWorkspace = styledAttrs.getBoolean(R.styleable.SizeSpec_matchWorkspace, false) + val maxSize = + styledAttrs.getDimensionPixelSize(R.styleable.SizeSpec_maxSize, Int.MAX_VALUE) + + styledAttrs.recycle() + + return SizeSpec(fixedSize, ofAvailableSpace, ofRemainderSpace, matchWorkspace, maxSize) + } + } +} diff --git a/src/com/android/launcher3/responsive/WorkspaceSpecs.kt b/src/com/android/launcher3/responsive/WorkspaceSpecs.kt new file mode 100644 index 0000000000..0da7026bd6 --- /dev/null +++ b/src/com/android/launcher3/responsive/WorkspaceSpecs.kt @@ -0,0 +1,98 @@ +/* + * 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.launcher3.responsive + +import android.content.res.TypedArray +import android.util.Log +import com.android.launcher3.R +import com.android.launcher3.responsive.ResponsiveSpec.SpecType +import com.android.launcher3.util.ResourceHelper + +private const val TAG = "WorkspaceSpecs" + +class WorkspaceSpecs(widthSpecs: List<WorkspaceSpec>, heightSpecs: List<WorkspaceSpec>) : + ResponsiveSpecs<WorkspaceSpec>(widthSpecs, heightSpecs) { + + fun getCalculatedWidthSpec(columns: Int, availableWidth: Int): CalculatedWorkspaceSpec { + val spec = getWidthSpec(availableWidth) + return CalculatedWorkspaceSpec(availableWidth, columns, spec) + } + + fun getCalculatedHeightSpec(rows: Int, availableHeight: Int): CalculatedWorkspaceSpec { + val spec = getHeightSpec(availableHeight) + return CalculatedWorkspaceSpec(availableHeight, rows, spec) + } + + companion object { + private const val XML_WORKSPACE_SPEC = "workspaceSpec" + + @JvmStatic + fun create(resourceHelper: ResourceHelper): WorkspaceSpecs { + val parser = ResponsiveSpecsParser(resourceHelper) + val specs = parser.parseXML(XML_WORKSPACE_SPEC, ::WorkspaceSpec) + val (widthSpecs, heightSpecs) = specs.partition { it.specType == SpecType.WIDTH } + return WorkspaceSpecs(widthSpecs, heightSpecs) + } + } +} + +data class WorkspaceSpec( + override val maxAvailableSize: Int, + override val specType: SpecType, + override val startPadding: SizeSpec, + override val endPadding: SizeSpec, + override val gutter: SizeSpec, + override val cellSize: SizeSpec +) : ResponsiveSpec(maxAvailableSize, specType, startPadding, endPadding, gutter, cellSize) { + + init { + check(isValid()) { "Invalid WorkspaceSpec found." } + } + + constructor( + attrs: TypedArray, + specs: Map<String, SizeSpec> + ) : this( + maxAvailableSize = + attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0), + specType = + SpecType.values()[ + attrs.getInt(R.styleable.ResponsiveSpec_specType, SpecType.HEIGHT.ordinal)], + startPadding = specs.getOrError(SizeSpec.XmlTags.START_PADDING), + endPadding = specs.getOrError(SizeSpec.XmlTags.END_PADDING), + gutter = specs.getOrError(SizeSpec.XmlTags.GUTTER), + cellSize = specs.getOrError(SizeSpec.XmlTags.CELL_SIZE) + ) + + override fun isValid(): Boolean { + // Workspace spec should not match workspace + if ( + startPadding.matchWorkspace || + endPadding.matchWorkspace || + gutter.matchWorkspace || + cellSize.matchWorkspace + ) { + Log.e(TAG, "WorkspaceSpec#isValid - workspace shouldn't contain matchWorkspace!") + return false + } + + return super.isValid() + } +} + +class CalculatedWorkspaceSpec(availableSpace: Int, cells: Int, spec: WorkspaceSpec) : + CalculatedResponsiveSpec(availableSpace, cells, spec) diff --git a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java index f03c62ac5c..2d69bfad20 100644 --- a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java +++ b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java @@ -60,13 +60,15 @@ public class PinnedAppsAdapter extends BaseAdapter implements OnSharedPreference private final OnClickListener mOnClickListener; private final OnLongClickListener mOnLongClickListener; private final SharedPreferences mPrefs; - private final AllAppsStore mAllAppsList; + private final AllAppsStore<SecondaryDisplayLauncher> mAllAppsList; private final AppInfoComparator mAppNameComparator; private final Set<ComponentKey> mPinnedApps = new HashSet<>(); private final ArrayList<AppInfo> mItems = new ArrayList<>(); - public PinnedAppsAdapter(SecondaryDisplayLauncher launcher, AllAppsStore allAppsStore, + public PinnedAppsAdapter( + SecondaryDisplayLauncher launcher, + AllAppsStore<SecondaryDisplayLauncher> allAppsStore, OnLongClickListener onLongClickListener) { mLauncher = launcher; mOnClickListener = launcher.getItemOnClickListener(); diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java index 458f137949..a10c0ad842 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java @@ -72,7 +72,7 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity implements BgDataModel.Callbacks, DragController.DragListener { private LauncherModel mModel; - private BaseDragLayer mDragLayer; + private SecondaryDragLayer mDragLayer; private SecondaryDragController mDragController; private ActivityAllAppsContainerView<SecondaryDisplayLauncher> mAppsView; private View mAppsButton; @@ -302,7 +302,7 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity public void bindAllApplications(AppInfo[] apps, int flags, Map<PackageUserKey, Integer> packageUserKeytoUidMap) { Preconditions.assertUIThread(); - AllAppsStore appsStore = mAppsView.getAppsStore(); + AllAppsStore<SecondaryDisplayLauncher> appsStore = mAppsView.getAppsStore(); appsStore.setApps(apps, flags, packageUserKeytoUidMap); PopupContainerWithArrow.dismissInvalidPopup(this); } @@ -314,10 +314,6 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity } } - public SecondaryDisplayPredictions getSecondaryDisplayPredictions() { - return mSecondaryDisplayPredictions; - } - @Override public StringCache getStringCache() { return mStringCache; @@ -337,6 +333,11 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity return this::onIconClicked; } + @Override + public View.OnLongClickListener getAllAppsItemLongClickListener() { + return v -> mDragLayer.onIconLongClicked(v); + } + private void onIconClicked(View v) { // Make sure that rogue clicks don't get through while allapps is launching, or after the // view has detached (it's possible for this to happen if the view is removed mid touch). diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java index 21c50d38e8..a58916ad80 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java @@ -16,10 +16,8 @@ package com.android.launcher3.secondarydisplay; import android.content.Context; -import android.view.View; import com.android.launcher3.R; -import com.android.launcher3.allapps.ActivityAllAppsContainerView; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.util.ResourceBasedOverride; @@ -47,12 +45,4 @@ public class SecondaryDisplayPredictions implements ResourceBasedOverride { */ public void setPredictedApps(BgDataModel.FixedContainerItems item) { } - - /** - * Set long click listener for predicted apps in top of app drawer. - */ - public void setLongClickListener( - ActivityAllAppsContainerView<?> appsView, - View.OnLongClickListener onIconLongClickListener) { - } } diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java index 87afcabdf8..e8be12cba6 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java @@ -79,9 +79,6 @@ public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher> mAllAppsButton = findViewById(R.id.all_apps_button); mAppsView = findViewById(R.id.apps_view); - mAppsView.setOnIconLongClickListener(this::onIconLongClicked); - mActivity.getSecondaryDisplayPredictions() - .setLongClickListener(mAppsView, this::onIconLongClicked); // Setup workspace mWorkspace = findViewById(R.id.workspace_grid); mPinnedAppsAdapter = new PinnedAppsAdapter(mActivity, mAppsView.getAppsStore(), @@ -179,7 +176,7 @@ public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher> return mPinnedAppsAdapter; } - private boolean onIconLongClicked(View v) { + boolean onIconLongClicked(View v) { if (!(v instanceof BubbleTextView)) { return false; } @@ -243,9 +240,8 @@ public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher> public void onPreDragStart(DropTarget.DragObject dragObject) { mDragView = dragObject.dragView; if (!shouldStartDrag(0)) { - mDragView.setOnAnimationEndCallback(() -> { - mActivity.beginDragShared(v, mActivity.getAppsView(), options); - }); + mDragView.setOnScaleAnimEndCallback(() -> + mActivity.beginDragShared(v, mActivity.getAppsView(), options)); } } diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java index 198dad3c89..360ff7ea25 100644 --- a/src/com/android/launcher3/statemanager/StateManager.java +++ b/src/com/android/launcher3/statemanager/StateManager.java @@ -27,6 +27,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.os.Handler; import android.os.Looper; +import android.util.Log; import androidx.annotation.FloatRange; @@ -35,6 +36,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.states.StateAnimationConfig.AnimationFlags; +import com.android.launcher3.testing.shared.TestProtocol; import java.io.PrintWriter; import java.util.ArrayList; @@ -76,6 +78,10 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> { return mState; } + public STATE_TYPE getTargetState() { + return (STATE_TYPE) mConfig.targetState; + } + public STATE_TYPE getCurrentStableState() { return mCurrentStableState; } @@ -221,6 +227,8 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> { private void goToState( STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) { + Log.d(TestProtocol.OVERVIEW_OVER_HOME, "go to state " + state); + animated &= areAnimatorsEnabled(); if (mActivity.isInState(state)) { if (mConfig.currentAnimation == null) { @@ -375,6 +383,8 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> { mState = state; mActivity.onStateSetStart(mState); + Log.d(TestProtocol.OVERVIEW_OVER_HOME, "Notifying listeners for state transition start" + + " to state: " + state.toString()); for (int i = mListeners.size() - 1; i >= 0; i--) { mListeners.get(i).onStateTransitionStart(state); } @@ -392,6 +402,8 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> { setRestState(null); } + Log.d(TestProtocol.OVERVIEW_OVER_HOME, "Notifying " + mListeners.size() + " listeners " + + "for end transition for state: " + state.toString()); for (int i = mListeners.size() - 1; i >= 0; i--) { mListeners.get(i).onStateTransitionComplete(state); } @@ -429,6 +441,7 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> { * Cancels the current animation. */ public void cancelAnimation() { + Log.d(TestProtocol.OVERVIEW_OVER_HOME, "current animation cancelled"); mConfig.reset(); // It could happen that a new animation is set as a result of an endListener on the // existing animation. @@ -452,6 +465,7 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> { * @param toState The state we are animating towards. */ public void setCurrentAnimation(AnimatorSet anim, STATE_TYPE toState) { + Log.d(TestProtocol.OVERVIEW_OVER_HOME, "setting animation to " + toState.toString()); cancelAnimation(); setCurrentAnimation(anim); anim.addListener(createStateAnimationListener(toState)); diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java index d1e816bec6..0d9e01035e 100644 --- a/src/com/android/launcher3/states/StateAnimationConfig.java +++ b/src/com/android/launcher3/states/StateAnimationConfig.java @@ -66,7 +66,8 @@ public class StateAnimationConfig { ANIM_WORKSPACE_PAGE_TRANSLATE_X, ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN, ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE, - ANIM_ALL_APPS_BOTTOM_SHEET_FADE + ANIM_ALL_APPS_BOTTOM_SHEET_FADE, + ANIM_ALL_APPS_KEYBOARD_FADE }) @Retention(RetentionPolicy.SOURCE) public @interface AnimType {} @@ -90,8 +91,9 @@ public class StateAnimationConfig { public static final int ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN = 17; public static final int ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE = 18; public static final int ANIM_ALL_APPS_BOTTOM_SHEET_FADE = 19; + public static final int ANIM_ALL_APPS_KEYBOARD_FADE = 20; - private static final int ANIM_TYPES_COUNT = 20; + private static final int ANIM_TYPES_COUNT = 21; protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT]; diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index e62ccbc208..07a1b82f92 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -16,6 +16,7 @@ package com.android.launcher3.testing; import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST; +import static com.android.launcher3.config.FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW; import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; @@ -203,10 +204,11 @@ public class TestInformationHandler implements ResourceBasedOverride { } case TestProtocol.REQUEST_WORKSPACE_COLUMNS_ROWS: { + InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext); return getLauncherUIProperty(Bundle::putParcelable, launcher -> new Point( - InvariantDeviceProfile.INSTANCE.get(mContext).numColumns, - InvariantDeviceProfile.INSTANCE.get(mContext).numRows) - ); + idp.getDeviceProfile(mContext).getPanelCount() * idp.numColumns, + idp.numRows + )); } case TestProtocol.REQUEST_WORKSPACE_CURRENT_PAGE_INDEX: { @@ -229,7 +231,7 @@ public class TestInformationHandler implements ResourceBasedOverride { } case TestProtocol.REQUEST_HAS_TIS: { - response.putBoolean(TestProtocol.REQUEST_HAS_TIS, false); + response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, false); return response; } @@ -245,6 +247,12 @@ public class TestInformationHandler implements ResourceBasedOverride { + l.getAppsView().getActiveRecyclerView().getPaddingBottom()); } + case TestProtocol.REQUEST_FLAG_ENABLE_GRID_ONLY_OVERVIEW: { + response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, + ENABLE_GRID_ONLY_OVERVIEW.get()); + return response; + } + default: return null; } diff --git a/src/com/android/launcher3/testing/TestLogging.java b/src/com/android/launcher3/testing/TestLogging.java index f95548db2f..70691f82de 100644 --- a/src/com/android/launcher3/testing/TestLogging.java +++ b/src/com/android/launcher3/testing/TestLogging.java @@ -27,26 +27,29 @@ import com.android.launcher3.testing.shared.TestProtocol; import java.util.function.BiConsumer; public final class TestLogging { + private static final String TAPL_EVENTS_TAG = "TaplEvents"; + private static final String LAUNCHER_EVENTS_TAG = "LauncherEvents"; private static BiConsumer<String, String> sEventConsumer; public static boolean sHadEventsNotFromTest; - private static void recordEventSlow(String sequence, String event) { - Log.d(TestProtocol.TAPL_EVENTS_TAG, sequence + " / " + event); + private static void recordEventSlow(String sequence, String event, boolean reportToTapl) { + Log.d(reportToTapl ? TAPL_EVENTS_TAG : LAUNCHER_EVENTS_TAG, + sequence + " / " + event); final BiConsumer<String, String> eventConsumer = sEventConsumer; - if (eventConsumer != null) { + if (reportToTapl && eventConsumer != null) { eventConsumer.accept(sequence, event); } } public static void recordEvent(String sequence, String event) { if (Utilities.isRunningInTestHarness()) { - recordEventSlow(sequence, event); + recordEventSlow(sequence, event, true); } } public static void recordEvent(String sequence, String message, Object parameter) { if (Utilities.isRunningInTestHarness()) { - recordEventSlow(sequence, message + ": " + parameter); + recordEventSlow(sequence, message + ": " + parameter, true); } } @@ -59,14 +62,20 @@ public final class TestLogging { public static void recordKeyEvent(String sequence, String message, KeyEvent event) { if (Utilities.isRunningInTestHarness()) { - recordEventSlow(sequence, message + ": " + event); + recordEventSlow(sequence, message + ": " + event, true); registerEventNotFromTest(event); } } public static void recordMotionEvent(String sequence, String message, MotionEvent event) { - if (Utilities.isRunningInTestHarness() && event.getAction() != MotionEvent.ACTION_MOVE) { - recordEventSlow(sequence, message + ": " + event); + final int action = event.getAction(); + if (Utilities.isRunningInTestHarness() && action != MotionEvent.ACTION_MOVE) { + // "Expecting" in TAPL motion events was thought to be producing considerable noise in + // tests due to failed checks for expected events. So we are not sending them to TAPL. + // Other events, such as EVENT_PILFER_POINTERS produce less noise and are thought to + // be more useful. + // That's why we pass false as the value for the 'reportToTapl' parameter. + recordEventSlow(sequence, message + ": " + event, false); registerEventNotFromTest(event); } } diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index c499e35ddd..cec4574fd8 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.touch; +import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.LauncherAnimUtils.TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.LauncherAnimUtils.newCancelListener; @@ -22,7 +23,6 @@ import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; -import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW; @@ -384,8 +384,8 @@ public abstract class AbstractStateChangeTouchController } else { logReachedState(targetState); } - mLauncher.getRootView().getSysUiScrim().createSysuiMultiplierAnim( - 1f).setDuration(0).start(); + mLauncher.getRootView().getSysUiScrim().getSysUIMultiplier().animateToValue(1f) + .setDuration(0).start(); } private void logReachedState(LauncherState targetState) { diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java index d028f24b0a..447d22b591 100644 --- a/src/com/android/launcher3/touch/AllAppsSwipeController.java +++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java @@ -15,17 +15,20 @@ */ package com.android.launcher3.touch; +import static com.android.app.animation.Interpolators.DECELERATED_EASE; +import static com.android.app.animation.Interpolators.EMPHASIZED; +import static com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE; +import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE; +import static com.android.app.animation.Interpolators.FINAL_FRAME; +import static com.android.app.animation.Interpolators.INSTANT; +import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.clampToProgress; +import static com.android.app.animation.Interpolators.mapToProgress; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE; -import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; -import static com.android.launcher3.anim.Interpolators.INSTANT; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.clampToProgress; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_KEYBOARD_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH; import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE; @@ -40,11 +43,11 @@ import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; import android.view.MotionEvent; import android.view.animation.Interpolator; +import com.android.app.animation.Interpolators; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.states.StateAnimationConfig; /** @@ -209,8 +212,8 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController { if (!config.userControlled) { config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED); } - config.setInterpolator(ANIM_WORKSPACE_SCALE, EMPHASIZED); - config.setInterpolator(ANIM_DEPTH, EMPHASIZED); + config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATED_EASE); + config.setInterpolator(ANIM_DEPTH, DECELERATED_EASE); } else { if (config.userControlled) { config.setInterpolator(ANIM_DEPTH, Interpolators.reverse(BLUR_MANUAL)); @@ -250,8 +253,8 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController { if (!config.userControlled) { config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED); } - config.setInterpolator(ANIM_WORKSPACE_SCALE, EMPHASIZED); - config.setInterpolator(ANIM_DEPTH, EMPHASIZED); + config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATED_EASE); + config.setInterpolator(ANIM_DEPTH, DECELERATED_EASE); } else { config.setInterpolator(ANIM_DEPTH, config.userControlled ? BLUR_MANUAL : BLUR_ATOMIC); config.setInterpolator(ANIM_WORKSPACE_FADE, @@ -293,20 +296,15 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController { config.setInterpolator(ANIM_WORKSPACE_SCALE, INSTANT); config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, INSTANT); } else { - // Remove scrim for this transition. - config.setInterpolator(ANIM_SCRIM_FADE, progress -> 0); - - // For now, pop the background panel in at full opacity at the threshold. + // Pop the background panel, keyboard, and content in at full opacity at the threshold. config.setInterpolator(ANIM_ALL_APPS_BOTTOM_SHEET_FADE, thresholdInterpolator(threshold, INSTANT)); - - // Fade the apps in when the scrim normally does, so it's apparent sooner what is - // happening (in this case we are fading them on top of the background panel). - config.setInterpolator(ANIM_ALL_APPS_FADE, - thresholdInterpolator(threshold, SCRIM_FADE_MANUAL)); + config.setInterpolator(ANIM_ALL_APPS_KEYBOARD_FADE, + thresholdInterpolator(threshold, INSTANT)); + config.setInterpolator(ANIM_ALL_APPS_FADE, thresholdInterpolator(threshold, INSTANT)); config.setInterpolator(ANIM_VERTICAL_PROGRESS, - thresholdInterpolator(threshold, ALL_APPS_VERTICAL_PROGRESS_MANUAL)); + thresholdInterpolator(threshold, mapToProgress(LINEAR, threshold, 1f))); } } diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java index 790c226a11..8c12547bb5 100644 --- a/src/com/android/launcher3/touch/ItemClickHandler.java +++ b/src/com/android/launcher3/touch/ItemClickHandler.java @@ -42,6 +42,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.apppairs.AppPairIcon; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.logging.InstanceId; @@ -95,6 +96,8 @@ public class ItemClickHandler { } else if (tag instanceof FolderInfo) { if (v instanceof FolderIcon) { onClickFolderIcon(v); + } else if (v instanceof AppPairIcon) { + onClickAppPairIcon(v); } } else if (tag instanceof AppInfo) { startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher); @@ -123,6 +126,17 @@ public class ItemClickHandler { } /** + * Event handler for an app pair icon click. + * + * @param v The view that was clicked. Must be an instance of {@link AppPairIcon}. + */ + private static void onClickAppPairIcon(View v) { + Launcher launcher = Launcher.getLauncher(v.getContext()); + FolderInfo folderInfo = ((AppPairIcon) v).getInfo(); + launcher.launchAppPair(folderInfo.contents.get(0), folderInfo.contents.get(1)); + } + + /** * Event handler for the app widget view which has not fully restored. */ private static void onClickPendingWidget(PendingAppWidgetHostView v, Launcher launcher) { diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java index 9cba19d7ed..122b1e0d7e 100644 --- a/src/com/android/launcher3/touch/ItemLongClickListener.java +++ b/src/com/android/launcher3/touch/ItemLongClickListener.java @@ -67,7 +67,7 @@ public class ItemLongClickListener { if (!(v.getTag() instanceof ItemInfo)) return false; launcher.setWaitingForResult(null); - beginDrag(v, launcher, (ItemInfo) v.getTag(), launcher.getDefaultWorkspaceDragOptions()); + beginDrag(v, launcher, (ItemInfo) v.getTag(), new DragOptions()); return true; } diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java index 6a972eb0dd..dc4621e0c1 100644 --- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -582,7 +582,8 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { ? splitInfo.dividerHeightPercent : splitInfo.dividerWidthPercent; - float scale = (float) outRect.height() / dp.availableHeightPx; + int taskbarHeight = dp.isTransientTaskbar ? 0 : dp.taskbarHeight; + float scale = (float) outRect.height() / (dp.availableHeightPx - taskbarHeight); float topTaskHeight = dp.availableHeightPx * topLeftTaskPercent; float scaledTopTaskHeight = topTaskHeight * scale; float dividerHeight = dp.availableHeightPx * dividerBarPercent; @@ -639,7 +640,8 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { // Reset unused translations primarySnapshot.setTranslationY(0); } else { - float scale = (float) totalThumbnailHeight / dp.availableHeightPx; + int taskbarHeight = dp.isTransientTaskbar ? 0 : dp.taskbarHeight; + float scale = (float) totalThumbnailHeight / (dp.availableHeightPx - taskbarHeight); float topTaskHeight = dp.availableHeightPx * taskPercent; float finalDividerHeight = Math.round(totalThumbnailHeight * dividerScale); float scaledTopTaskHeight = topTaskHeight * scale; diff --git a/src/com/android/launcher3/util/CannedAnimationCoordinator.kt b/src/com/android/launcher3/util/CannedAnimationCoordinator.kt new file mode 100644 index 0000000000..18f833978a --- /dev/null +++ b/src/com/android/launcher3/util/CannedAnimationCoordinator.kt @@ -0,0 +1,164 @@ +/* + * 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.launcher3.util + +import android.animation.AnimatorSet +import android.animation.ValueAnimator +import android.util.Log +import android.view.ViewTreeObserver.OnGlobalLayoutListener +import androidx.core.view.OneShotPreDrawListener +import com.android.app.animation.Interpolators.LINEAR +import com.android.launcher3.anim.AnimatorListeners +import com.android.launcher3.anim.AnimatorPlaybackController +import com.android.launcher3.anim.PendingAnimation +import com.android.launcher3.statemanager.StatefulActivity +import java.util.function.Consumer + +private const val TAG = "CannedAnimCoordinator" + +/** + * Utility class to run a canned animation on Launcher. + * + * This class takes care to registering animations with stateManager and ensures that only one + * animation is playing at a time. + */ +class CannedAnimationCoordinator(private val activity: StatefulActivity<*>) { + + private val launcherLayoutListener = OnGlobalLayoutListener { scheduleRecreateAnimOnPreDraw() } + private var recreatePending = false + + private var animationProvider: Any? = null + + private var animationDuration: Long = 0L + private var animationFactory: Consumer<PendingAnimation>? = null + private var animationController: AnimatorPlaybackController? = null + + private var currentAnim: AnimatorPlaybackController? = null + + /** + * Sets the current animation cancelling any previously set animation. + * + * Callers can control the animation using {@link #getPlaybackController}. The state is + * automatically cleared when the playback controller ends. The animation is automatically + * recreated when any layout change happens. Callers can also ask for recreation by calling + * {@link #recreateAnimation} + */ + fun setAnimation(provider: Any, factory: Consumer<PendingAnimation>, duration: Long) { + if (provider != animationProvider) { + Log.e(TAG, "Trying to play two animations together, $provider and $animationProvider") + } + + // Cancel any previously running animation + endCurrentAnimation(false) + animationController?.dispatchOnCancel()?.dispatchOnEnd() + + animationProvider = provider + animationFactory = factory + animationDuration = duration + + // Setup a new controller and link it with launcher state animation + val anim = AnimatorSet() + anim.play( + ValueAnimator.ofFloat(0f, 1f).apply { + interpolator = LINEAR + this.duration = duration + addUpdateListener { anim -> currentAnim?.setPlayFraction(anim.animatedFraction) } + } + ) + val controller = AnimatorPlaybackController.wrap(anim, duration) + anim.addListener( + AnimatorListeners.forEndCallback { success -> + if (animationController != controller) { + return@forEndCallback + } + + endCurrentAnimation(success) + animationController = null + animationFactory = null + animationProvider = null + + activity.rootView.viewTreeObserver.apply { + if (isAlive) { + removeOnGlobalLayoutListener(launcherLayoutListener) + } + } + } + ) + + // Recreate animation whenever layout happens in case transforms change during layout + activity.rootView.viewTreeObserver.apply { + if (isAlive) { + addOnGlobalLayoutListener(launcherLayoutListener) + } + } + // Link this to the state manager so that it auto-cancels when state changes + recreatePending = false + animationController = + controller.apply { activity.stateManager.setCurrentUserControlledAnimation(this) } + recreateAnimation(provider) + } + + private fun endCurrentAnimation(success: Boolean) { + currentAnim?.apply { + // When cancelling an animation, apply final progress so that all transformations + // are restored + setPlayFraction(1f) + if (!success) dispatchOnCancel() + dispatchOnEnd() + } + currentAnim = null + } + + /** Returns the current animation controller to control the animation */ + fun getPlaybackController(provider: Any): AnimatorPlaybackController? { + return if (provider == animationProvider) animationController + else { + Log.d(TAG, "Wrong controller access from $provider, actual provider $animationProvider") + null + } + } + + private fun scheduleRecreateAnimOnPreDraw() { + if (!recreatePending) { + recreatePending = true + OneShotPreDrawListener.add(activity.rootView) { + if (recreatePending) { + recreatePending = false + animationProvider?.apply { recreateAnimation(this) } + } + } + } + } + + /** Notify the controller to recreate the animation. The animation progress is preserved */ + fun recreateAnimation(provider: Any) { + if (provider != animationProvider) { + Log.e(TAG, "Ignore recreate request from $provider, actual provider $animationProvider") + return + } + endCurrentAnimation(false /* success */) + + if (animationFactory == null || animationController == null) { + return + } + currentAnim = + PendingAnimation(animationDuration) + .apply { animationFactory?.accept(this) } + .createPlaybackController() + .apply { setPlayFraction(animationController!!.progressFraction) } + } +} diff --git a/src/com/android/launcher3/util/DimensionUtils.kt b/src/com/android/launcher3/util/DimensionUtils.kt index 9188c2ead4..0eb0e087ee 100644 --- a/src/com/android/launcher3/util/DimensionUtils.kt +++ b/src/com/android/launcher3/util/DimensionUtils.kt @@ -29,9 +29,9 @@ object DimensionUtils { */ @JvmStatic fun getTaskbarPhoneDimensions( - deviceProfile: DeviceProfile, - res: Resources, - isPhoneMode: Boolean + deviceProfile: DeviceProfile, + res: Resources, + isPhoneMode: Boolean ): Point { val p = Point() // Taskbar for large screen diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java index 6647d0db7c..a7c94bb726 100644 --- a/src/com/android/launcher3/util/DisplayController.java +++ b/src/com/android/launcher3/util/DisplayController.java @@ -54,8 +54,8 @@ import com.android.launcher3.util.window.WindowManagerProxy; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -106,7 +106,8 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { private final LauncherPrefs mPrefs; - private DisplayController(Context context) { + @VisibleForTesting + protected DisplayController(Context context) { mContext = context; mDM = context.getSystemService(DisplayManager.class); mPrefs = LauncherPrefs.get(context); @@ -127,8 +128,7 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { Context displayInfoContext = getDisplayInfoContext(display); mInfo = new Info(displayInfoContext, wmProxy, wmProxy.estimateInternalDisplayBounds(displayInfoContext)); - mInfo.mPerDisplayBounds.forEach((key, value) -> FileLog.i(TAG, - "(CTOR) perDisplayBounds - " + key + ": " + Arrays.deepToString(value))); + FileLog.i(TAG, "(CTOR) perDisplayBounds: " + mInfo.mPerDisplayBounds); } /** @@ -286,9 +286,8 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds) || !newInfo.mPerDisplayBounds.equals(oldInfo.mPerDisplayBounds)) { change |= CHANGE_SUPPORTED_BOUNDS; - newInfo.mPerDisplayBounds.forEach((key, value) -> FileLog.w(TAG, - "(CHANGE_SUPPORTED_BOUNDS) perDisplayBounds - " + key + ": " - + Arrays.deepToString(value))); + FileLog.w(TAG, + "(CHANGE_SUPPORTED_BOUNDS) perDisplayBounds: " + newInfo.mPerDisplayBounds); } if (DEBUG) { Log.d(TAG, "handleInfoChange - change: " + getChangeFlagsString(change)); @@ -329,7 +328,7 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { // WindowBounds public final WindowBounds realBounds; public final Set<WindowBounds> supportedBounds = new ArraySet<>(); - private final ArrayMap<CachedDisplayInfo, WindowBounds[]> mPerDisplayBounds = + private final ArrayMap<CachedDisplayInfo, List<WindowBounds>> mPerDisplayBounds = new ArrayMap<>(); public Info(Context displayInfoContext) { @@ -340,7 +339,7 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { // Used for testing public Info(Context displayInfoContext, WindowManagerProxy wmProxy, - Map<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache) { + Map<CachedDisplayInfo, List<WindowBounds>> perDisplayBoundsCache) { CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(displayInfoContext); normalizedDisplayInfo = displayInfo.normalize(); rotation = displayInfo.rotation; @@ -354,16 +353,14 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { navigationMode = wmProxy.getNavigationMode(displayInfoContext); mPerDisplayBounds.putAll(perDisplayBoundsCache); - WindowBounds[] cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo); + List<WindowBounds> cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo); realBounds = wmProxy.getRealBounds(displayInfoContext, displayInfo); if (cachedValue == null) { // Unexpected normalizedDisplayInfo is found, recreate the cache FileLog.e(TAG, "Unexpected normalizedDisplayInfo found, invalidating cache: " + normalizedDisplayInfo); - mPerDisplayBounds.forEach((key, value) -> FileLog.e(TAG, - "(Invalid Cache) perDisplayBounds - " + key + ": " + Arrays.deepToString( - value))); + FileLog.e(TAG, "(Invalid Cache) perDisplayBounds : " + mPerDisplayBounds); mPerDisplayBounds.clear(); mPerDisplayBounds.putAll(wmProxy.estimateInternalDisplayBounds(displayInfoContext)); cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo); @@ -376,22 +373,19 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { if (cachedValue != null) { // Verify that the real bounds are a match - WindowBounds expectedBounds = cachedValue[displayInfo.rotation]; + WindowBounds expectedBounds = cachedValue.get(displayInfo.rotation); if (!realBounds.equals(expectedBounds)) { - WindowBounds[] clone = new WindowBounds[4]; - System.arraycopy(cachedValue, 0, clone, 0, 4); - clone[displayInfo.rotation] = realBounds; + List<WindowBounds> clone = new ArrayList<>(cachedValue); + clone.set(displayInfo.rotation, realBounds); mPerDisplayBounds.put(normalizedDisplayInfo, clone); } } - mPerDisplayBounds.values().forEach( - windowBounds -> Collections.addAll(supportedBounds, windowBounds)); + mPerDisplayBounds.values().forEach(supportedBounds::addAll); if (DEBUG) { Log.d(TAG, "displayInfo: " + displayInfo); Log.d(TAG, "realBounds: " + realBounds); Log.d(TAG, "normalizedDisplayInfo: " + normalizedDisplayInfo); - mPerDisplayBounds.forEach((key, value) -> Log.d(TAG, - "perDisplayBounds - " + key + ": " + Arrays.deepToString(value))); + Log.d(TAG, "perDisplayBounds: " + mPerDisplayBounds); } } @@ -448,7 +442,7 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { pw.println(" navigationMode=" + info.navigationMode.name()); pw.println(" currentSize=" + info.currentSize); info.mPerDisplayBounds.forEach((key, value) -> pw.println( - " perDisplayBounds - " + key + ": " + Arrays.deepToString(value))); + " perDisplayBounds - " + key + ": " + value)); } /** diff --git a/src/com/android/launcher3/util/EventLogArray.kt b/src/com/android/launcher3/util/EventLogArray.kt new file mode 100644 index 0000000000..a17d6509e3 --- /dev/null +++ b/src/com/android/launcher3/util/EventLogArray.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.launcher3.util + +import java.io.PrintWriter +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +/** + * A utility class to record and log events. Events are stored in a fixed size array and old logs + * are purged as new events come. + */ +class EventLogArray(private val name: String, size: Int) { + + companion object { + private const val TYPE_ONE_OFF = 0 + private const val TYPE_FLOAT = 1 + private const val TYPE_INTEGER = 2 + private const val TYPE_BOOL_TRUE = 3 + private const val TYPE_BOOL_FALSE = 4 + private fun isEntrySame(entry: EventEntry?, type: Int, event: String): Boolean { + return entry != null && entry.type == type && entry.event == event + } + } + + private val logs: Array<EventEntry?> + private var nextIndex = 0 + + init { + logs = arrayOfNulls(size) + } + + fun addLog(event: String) { + addLog(TYPE_ONE_OFF, event, 0f) + } + + fun addLog(event: String, extras: Int) { + addLog(TYPE_INTEGER, event, extras.toFloat()) + } + + fun addLog(event: String, extras: Float) { + addLog(TYPE_FLOAT, event, extras) + } + + fun addLog(event: String, extras: Boolean) { + addLog(if (extras) TYPE_BOOL_TRUE else TYPE_BOOL_FALSE, event, 0f) + } + + private fun addLog(type: Int, event: String, extras: Float) { + // Merge the logs if it's a duplicate + val last = (nextIndex + logs.size - 1) % logs.size + val secondLast = (nextIndex + logs.size - 2) % logs.size + if (isEntrySame(logs[last], type, event) && isEntrySame(logs[secondLast], type, event)) { + logs[last]!!.update(type, event, extras) + logs[secondLast]!!.duplicateCount++ + return + } + if (logs[nextIndex] == null) { + logs[nextIndex] = EventEntry() + } + logs[nextIndex]!!.update(type, event, extras) + nextIndex = (nextIndex + 1) % logs.size + } + + fun dump(prefix: String, writer: PrintWriter) { + writer.println("$prefix$name event history:") + val sdf = SimpleDateFormat(" HH:mm:ss.SSSZ ", Locale.US) + val date = Date() + for (i in logs.indices) { + val log = logs[(nextIndex + logs.size - i - 1) % logs.size] ?: continue + date.time = log.time + val msg = StringBuilder(prefix).append(sdf.format(date)).append(log.event) + when (log.type) { + TYPE_BOOL_FALSE -> msg.append(": false") + TYPE_BOOL_TRUE -> msg.append(": true") + TYPE_FLOAT -> msg.append(": ").append(log.extras) + TYPE_INTEGER -> msg.append(": ").append(log.extras.toInt()) + else -> {} + } + if (log.duplicateCount > 0) { + msg.append(" & ").append(log.duplicateCount).append(" similar events") + } + writer.println(msg) + } + } + + /** A single event entry. */ + private class EventEntry { + var type = 0 + var event: String? = null + var extras = 0f + var time: Long = 0 + var duplicateCount = 0 + fun update(type: Int, event: String, extras: Float) { + this.type = type + this.event = event + this.extras = extras + time = System.currentTimeMillis() + duplicateCount = 0 + } + } +} diff --git a/src/com/android/launcher3/util/Executors.java b/src/com/android/launcher3/util/Executors.java index 6978e0c2a4..dec4b5ca8d 100644 --- a/src/com/android/launcher3/util/Executors.java +++ b/src/com/android/launcher3/util/Executors.java @@ -21,6 +21,7 @@ import android.os.Process; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; @@ -58,6 +59,11 @@ public class Executors { new LooperExecutor( createAndStartNewLooper("UiThreadHelper", Process.THREAD_PRIORITY_FOREGROUND)); + + /** A background executor to preinflate views. */ + public static final ExecutorService VIEW_PREINFLATION_EXECUTOR = + java.util.concurrent.Executors.newSingleThreadExecutor(); + /** * Utility method to get a started handler thread statically */ diff --git a/src/com/android/launcher3/util/IconSizeSteps.kt b/src/com/android/launcher3/util/IconSizeSteps.kt new file mode 100644 index 0000000000..2a5afe0ce2 --- /dev/null +++ b/src/com/android/launcher3/util/IconSizeSteps.kt @@ -0,0 +1,47 @@ +/* + * 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.launcher3.util + +import android.content.res.Resources +import androidx.core.content.res.getDimensionOrThrow +import androidx.core.content.res.use +import com.android.launcher3.R +import kotlin.math.max + +class IconSizeSteps(res: Resources) { + private val steps: List<Int> + + init { + steps = + res.obtainTypedArray(R.array.icon_size_steps).use { + (0 until it.length()).map { step -> it.getDimensionOrThrow(step).toInt() }.sorted() + } + } + + fun minimumIconSize(): Int = steps[0] + + fun getNextLowerIconSize(iconSizePx: Int): Int { + return steps[max(0, getIndexForIconSize(iconSizePx) - 1)] + } + + fun getIconSmallerThan(cellWidth: Int): Int { + return steps.lastOrNull { it <= cellWidth } ?: steps[0] + } + + private fun getIndexForIconSize(iconSizePx: Int): Int { + return max(0, steps.indexOfFirst { iconSizePx <= it }) + } +} diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java index 6f706d2be1..bdb5e775ad 100644 --- a/src/com/android/launcher3/util/InstantAppResolver.java +++ b/src/com/android/launcher3/util/InstantAppResolver.java @@ -42,14 +42,7 @@ public class InstantAppResolver implements ResourceBasedOverride { return false; } - public boolean isInstantApp(Context context, String packageName) { - PackageManager packageManager = context.getPackageManager(); - try { - return isInstantApp(packageManager.getPackageInfo(packageName, 0).applicationInfo); - } catch (PackageManager.NameNotFoundException e) { - Log.e("InstantAppResolver", "Failed to determine whether package is instant app " - + packageName, e); - } + public boolean isInstantApp(String packageName, int userId) { return false; } } diff --git a/src/com/android/launcher3/util/IntArray.java b/src/com/android/launcher3/util/IntArray.java index 1c787959e4..249824216a 100644 --- a/src/com/android/launcher3/util/IntArray.java +++ b/src/com/android/launcher3/util/IntArray.java @@ -250,6 +250,11 @@ public class IntArray implements Cloneable, Iterable<Integer> { return b.toString(); } + @Override + public String toString() { + return "IntArray [" + toConcatString() + "]"; + } + public static IntArray fromConcatString(String concatString) { StringTokenizer tokenizer = new StringTokenizer(concatString, ","); int[] array = new int[tokenizer.countTokens()]; diff --git a/src/com/android/launcher3/util/LockedUserState.kt b/src/com/android/launcher3/util/LockedUserState.kt index 1231604780..0a87594fde 100644 --- a/src/com/android/launcher3/util/LockedUserState.kt +++ b/src/com/android/launcher3/util/LockedUserState.kt @@ -1,3 +1,18 @@ +/* + * 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.launcher3.util import android.content.Context diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java index 6a4e528368..1cb99944c0 100644 --- a/src/com/android/launcher3/util/MainThreadInitializedObject.java +++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java @@ -48,8 +48,8 @@ public class MainThreadInitializedObject<T> { } public T get(Context context) { - if (context instanceof SandboxContext) { - return ((SandboxContext) context).getObject(this, mProvider); + if (context instanceof SandboxContext sc) { + return sc.getObject(this); } if (mValue == null) { @@ -131,23 +131,22 @@ public class MainThreadInitializedObject<T> { * Find a cached object from mObjectMap if we have already created one. If not, generate * an object using the provider. */ - private <T> T getObject(MainThreadInitializedObject<T> object, ObjectProvider<T> provider) { + protected <T> T getObject(MainThreadInitializedObject<T> object) { synchronized (mDestroyLock) { if (mDestroyed) { Log.e(TAG, "Static object access with a destroyed context"); } - T t = (T) mObjectMap.get(object); if (t != null) { return t; } if (Looper.myLooper() == Looper.getMainLooper()) { - t = createObject(provider); + t = createObject(object); // Check if we've explicitly allowed the object or if it's a SafeCloseable, // it will get destroyed in onDestroy() if (!mAllowedObjects.contains(object) && !(t instanceof SafeCloseable)) { - throw new IllegalStateException( - "Leaking unknown objects " + object + " " + provider + " " + t); + throw new IllegalStateException("Leaking unknown objects " + + object + " " + object.mProvider + " " + t); } mObjectMap.put(object, t); mOrderedObjects.add(t); @@ -156,15 +155,15 @@ public class MainThreadInitializedObject<T> { } try { - return MAIN_EXECUTOR.submit(() -> getObject(object, provider)).get(); + return MAIN_EXECUTOR.submit(() -> getObject(object)).get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } } @UiThread - protected <T> T createObject(ObjectProvider<T> provider) { - return provider.get(this); + protected <T> T createObject(MainThreadInitializedObject<T> object) { + return object.mProvider.get(this); } } } diff --git a/src/com/android/launcher3/util/MultiScalePropertyFactory.java b/src/com/android/launcher3/util/MultiScalePropertyFactory.java index a7e6cc8679..cf8d6ccf67 100644 --- a/src/com/android/launcher3/util/MultiScalePropertyFactory.java +++ b/src/com/android/launcher3/util/MultiScalePropertyFactory.java @@ -40,8 +40,7 @@ public class MultiScalePropertyFactory<T extends View> { private static final boolean DEBUG = false; private static final String TAG = "MultiScaleProperty"; private final String mName; - private final ArrayMap<Integer, MultiScaleProperty> mProperties = - new ArrayMap<Integer, MultiScaleProperty>(); + private final ArrayMap<Integer, MultiScaleProperty> mProperties = new ArrayMap<>(); // This is an optimization for cases when set is called repeatedly with the same setterIndex. private float mMinOfOthers = 0; @@ -55,7 +54,7 @@ public class MultiScalePropertyFactory<T extends View> { } /** Returns the [MultiFloatProperty] associated with [inx], creating it if not present. */ - public MultiScaleProperty get(Integer index) { + public FloatProperty<T> get(Integer index) { return mProperties.computeIfAbsent(index, (k) -> new MultiScaleProperty(index, mName + "_" + index)); } diff --git a/src/com/android/launcher3/util/MultiValueAlpha.java b/src/com/android/launcher3/util/MultiValueAlpha.java index ac016a8595..a66a9d2d56 100644 --- a/src/com/android/launcher3/util/MultiValueAlpha.java +++ b/src/com/android/launcher3/util/MultiValueAlpha.java @@ -32,8 +32,15 @@ public class MultiValueAlpha extends MultiPropertyFactory<View> { // Whether we should change from INVISIBLE to VISIBLE and vice versa at low alpha values. private boolean mUpdateVisibility; + private final int mHiddenVisibility; + public MultiValueAlpha(View view, int size) { + this(view, size, View.INVISIBLE); + } + + public MultiValueAlpha(View view, int size, int hiddenVisibility) { super(view, VIEW_ALPHA, size, ALPHA_AGGREGATOR, 1f); + this.mHiddenVisibility = hiddenVisibility; } /** Sets whether we should update between INVISIBLE and VISIBLE based on alpha. */ @@ -45,7 +52,7 @@ public class MultiValueAlpha extends MultiPropertyFactory<View> { protected void apply(float value) { super.apply(value); if (mUpdateVisibility) { - AlphaUpdateListener.updateVisibility(mTarget); + AlphaUpdateListener.updateVisibility(mTarget, mHiddenVisibility); } } } diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java index 65736916d8..f8f4b5fc98 100644 --- a/src/com/android/launcher3/util/OnboardingPrefs.java +++ b/src/com/android/launcher3/util/OnboardingPrefs.java @@ -38,19 +38,13 @@ public class OnboardingPrefs<T extends ActivityContext> { public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count"; public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count"; public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen"; - public static final String SEARCH_KEYBOARD_EDU_SEEN = "launcher.search_edu_seen"; - public static final String SEARCH_SNACKBAR_COUNT = "launcher.keyboard_snackbar_count"; - public static final String SEARCH_ONBOARDING_COUNT = "launcher.search_onboarding_count"; public static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count"; - public static final String QSB_SEARCH_ONBOARDING_CARD_DISMISSED = "launcher.qsb_edu_dismiss"; public static final String TASKBAR_EDU_TOOLTIP_STEP = "launcher.taskbar_edu_tooltip_step"; // When adding a new key, add it here as well, to be able to reset it from Developer Options. public static final Map<String, String[]> ALL_PREF_KEYS = Map.of( "All Apps Bounce", new String[] { HOME_BOUNCE_SEEN, HOME_BOUNCE_COUNT }, "Hybrid Hotseat Education", new String[] { HOTSEAT_DISCOVERY_TIP_COUNT, HOTSEAT_LONGPRESS_TIP_SEEN }, - "Search Education", new String[] { SEARCH_KEYBOARD_EDU_SEEN, SEARCH_SNACKBAR_COUNT, - SEARCH_ONBOARDING_COUNT, QSB_SEARCH_ONBOARDING_CARD_DISMISSED}, "Taskbar Education", new String[] { TASKBAR_EDU_TOOLTIP_STEP }, "All Apps Visited Count", new String[] {ALL_APPS_VISITED_COUNT} ); @@ -61,8 +55,6 @@ public class OnboardingPrefs<T extends ActivityContext> { @StringDef(value = { HOME_BOUNCE_SEEN, HOTSEAT_LONGPRESS_TIP_SEEN, - SEARCH_KEYBOARD_EDU_SEEN, - QSB_SEARCH_ONBOARDING_CARD_DISMISSED }) @Retention(RetentionPolicy.SOURCE) public @interface EventBoolKey {} @@ -73,8 +65,6 @@ public class OnboardingPrefs<T extends ActivityContext> { @StringDef(value = { HOME_BOUNCE_COUNT, HOTSEAT_DISCOVERY_TIP_COUNT, - SEARCH_SNACKBAR_COUNT, - SEARCH_ONBOARDING_COUNT, ALL_APPS_VISITED_COUNT, TASKBAR_EDU_TOOLTIP_STEP, }) @@ -87,9 +77,6 @@ public class OnboardingPrefs<T extends ActivityContext> { Map<String, Integer> maxCounts = new ArrayMap<>(5); maxCounts.put(HOME_BOUNCE_COUNT, 3); maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5); - maxCounts.put(SEARCH_SNACKBAR_COUNT, 3); - // This is the sum of all onboarding cards. Currently there is only 1 card shown 3 times. - maxCounts.put(SEARCH_ONBOARDING_COUNT, 3); maxCounts.put(ALL_APPS_VISITED_COUNT, 20); maxCounts.put(TASKBAR_EDU_TOOLTIP_STEP, 2); MAX_COUNTS = Collections.unmodifiableMap(maxCounts); diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java index 1d6bc253ec..91203a7f9b 100644 --- a/src/com/android/launcher3/util/PackageManagerHelper.java +++ b/src/com/android/launcher3/util/PackageManagerHelper.java @@ -164,13 +164,6 @@ public class PackageManagerHelper { } } - public static Intent getStyleWallpapersIntent(Context context) { - return new Intent(Intent.ACTION_SET_WALLPAPER).setComponent( - new ComponentName(context.getString(R.string.wallpaper_picker_package), - context.getString(R.string.custom_activity_picker) - )); - } - /** * Starts the details activity for {@code info} */ diff --git a/src/com/android/launcher3/util/SettingsCache.java b/src/com/android/launcher3/util/SettingsCache.java index 0c5b7225d3..06cb00e3ae 100644 --- a/src/com/android/launcher3/util/SettingsCache.java +++ b/src/com/android/launcher3/util/SettingsCache.java @@ -154,7 +154,7 @@ public class SettingsCache extends ContentObserver implements SafeCloseable { */ public void unregister(Uri uri, OnChangeListener listener) { List<OnChangeListener> listenersToRemoveFrom = mListenerMap.get(uri); - if (!listenersToRemoveFrom.contains(listener)) { + if (listenersToRemoveFrom == null) { return; } diff --git a/quickstep/src/com/android/launcher3/proxy/StartActivityParams.java b/src/com/android/launcher3/util/StartActivityParams.java index b47ef47423..b48562f2d8 100644 --- a/quickstep/src/com/android/launcher3/proxy/StartActivityParams.java +++ b/src/com/android/launcher3/util/StartActivityParams.java @@ -14,13 +14,16 @@ * limitations under the License. */ -package com.android.launcher3.proxy; +package com.android.launcher3.util; import static android.app.PendingIntent.FLAG_MUTABLE; import static android.app.PendingIntent.FLAG_ONE_SHOT; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; +import static com.android.launcher3.Utilities.allowBGLaunch; + import android.app.Activity; +import android.app.ActivityOptions; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.content.Context; @@ -31,6 +34,9 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.Log; +/** + * Wrapper class for parameters to start an activity. + */ public class StartActivityParams implements Parcelable { private static final String TAG = "StartActivityParams"; @@ -90,10 +96,12 @@ public class StartActivityParams implements Parcelable { parcel.writeBundle(options); } + /** Perform the operation on the pendingIntent. */ public void deliverResult(Context context, int resultCode, Intent data) { + ActivityOptions options = allowBGLaunch(ActivityOptions.makeBasic()); try { if (mPICallback != null) { - mPICallback.send(context, resultCode, data); + mPICallback.send(context, resultCode, data, null, null, null, options.toBundle()); } } catch (CanceledException e) { Log.e(TAG, "Unable to send back result", e); @@ -101,7 +109,7 @@ public class StartActivityParams implements Parcelable { } public static final Parcelable.Creator<StartActivityParams> CREATOR = - new Parcelable.Creator<StartActivityParams>() { + new Parcelable.Creator<>() { public StartActivityParams createFromParcel(Parcel source) { return new StartActivityParams(source); } diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java index a5c663f72a..60951ba05a 100644 --- a/src/com/android/launcher3/util/Themes.java +++ b/src/com/android/launcher3/util/Themes.java @@ -21,8 +21,6 @@ import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_THEME; import static com.android.launcher3.LauncherPrefs.THEMED_ICONS; -import android.app.WallpaperColors; -import android.app.WallpaperManager; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; @@ -48,16 +46,9 @@ public class Themes { public static final String KEY_THEMED_ICONS = "themed_icons"; + /** Gets the WallpaperColorHints and then uses those to get the correct activity theme res. */ public static int getActivityThemeRes(Context context) { - final int colorHints; - if (Utilities.ATLEAST_P) { - WallpaperColors colors = context.getSystemService(WallpaperManager.class) - .getWallpaperColors(WallpaperManager.FLAG_SYSTEM); - colorHints = colors == null ? 0 : colors.getColorHints(); - } else { - colorHints = 0; - } - return getActivityThemeRes(context, colorHints); + return getActivityThemeRes(context, WallpaperColorHints.get(context).getHints()); } public static int getActivityThemeRes(Context context, int wallpaperColorHints) { diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java index c23df77db1..138cc4af3e 100644 --- a/src/com/android/launcher3/util/TraceHelper.java +++ b/src/com/android/launcher3/util/TraceHelper.java @@ -15,12 +15,17 @@ */ package com.android.launcher3.util; +import android.annotation.SuppressLint; import android.os.Trace; import androidx.annotation.MainThread; +import com.android.launcher3.Utilities; + import java.util.function.Supplier; +import kotlin.random.Random; + /** * A wrapper around {@link Trace} to allow better testing. * @@ -36,54 +41,63 @@ public class TraceHelper { // Temporarily ignore blocking binder calls for this trace. public static final int FLAG_IGNORE_BINDERS = 1 << 1; - public static final int FLAG_CHECK_FOR_RACE_CONDITIONS = 1 << 2; - - public static final int FLAG_UI_EVENT = - FLAG_ALLOW_BINDER_TRACKING | FLAG_CHECK_FOR_RACE_CONDITIONS; - /** * Static instance of Trace helper, overridden in tests. */ public static TraceHelper INSTANCE = new TraceHelper(); /** - * @return a token to pass into {@link #endSection(Object)}. + * @see Trace#beginSection(String) */ - public Object beginSection(String sectionName) { - return beginSection(sectionName, 0); - } - - public Object beginSection(String sectionName, int flags) { + public void beginSection(String sectionName) { Trace.beginSection(sectionName); - return null; } /** - * @param token the token returned from {@link #beginSection(String, int)} + * @see Trace#endSection() */ - public void endSection(Object token) { + public void endSection() { Trace.endSection(); } /** - * Similar to {@link #beginSection} but doesn't add a trace section. + * @see Trace#beginAsyncSection(String, int) + * @return a SafeCloseable that can be used to end the session */ - public Object beginFlagsOverride(int flags) { - return null; + @SuppressWarnings("NewApi") + @SuppressLint("NewApi") + public SafeCloseable beginAsyncSection(String sectionName) { + if (!Utilities.ATLEAST_Q) { + return () -> { }; + } + int cookie = Random.Default.nextInt(); + Trace.beginAsyncSection(sectionName, cookie); + return () -> Trace.endAsyncSection(sectionName, cookie); } - public void endFlagsOverride(Object token) { } + /** + * Returns a SafeCloseable to temporarily ignore blocking binder calls. + */ + @SuppressWarnings("NewApi") + @SuppressLint("NewApi") + public SafeCloseable allowIpcs(String rpcName) { + if (!Utilities.ATLEAST_Q) { + return () -> { }; + } + int cookie = Random.Default.nextInt(); + Trace.beginAsyncSection(rpcName, cookie); + return () -> Trace.endAsyncSection(rpcName, cookie); + } /** * Temporarily ignore blocking binder calls for the duration of this {@link Supplier}. + * + * Note, new features should be designed to not rely on mainThread RPCs. */ @MainThread public static <T> T allowIpcs(String rpcName, Supplier<T> supplier) { - Object traceToken = INSTANCE.beginSection(rpcName, FLAG_IGNORE_BINDERS); - try { + try (SafeCloseable c = INSTANCE.allowIpcs(rpcName)) { return supplier.get(); - } finally { - INSTANCE.endSection(traceToken); } } } diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java index ceba0db384..91945caa15 100644 --- a/src/com/android/launcher3/util/VibratorWrapper.java +++ b/src/com/android/launcher3/util/VibratorWrapper.java @@ -17,6 +17,7 @@ package com.android.launcher3.util; import static android.os.VibrationEffect.createPredefined; import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED; + import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; @@ -68,6 +69,9 @@ public class VibratorWrapper { @Nullable private final VibrationEffect mBumpEffect; + @Nullable + private final VibrationEffect mAssistEffect; + private long mLastDragTime; private final int mThresholdUntilNextDragCallMillis; @@ -125,12 +129,25 @@ public class VibratorWrapper { mBumpEffect = null; mThresholdUntilNextDragCallMillis = 0; } + + if (Utilities.ATLEAST_R && mVibrator.areAllPrimitivesSupported( + VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, + VibrationEffect.Composition.PRIMITIVE_TICK)) { + // quiet ramp, short pause, then sharp tick + mAssistEffect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50) + .compose(); + } else { + // fallback for devices without composition support + mAssistEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK); + } } /** - * This is called when the user swipes to/from all apps. This is meant to be used in between - * long animation progresses so that it gives a dragging texture effect. For a better - * experience, this should be used in combination with vibrateForDragCommit(). + * This is called when the user swipes to/from all apps. This is meant to be used in between + * long animation progresses so that it gives a dragging texture effect. For a better + * experience, this should be used in combination with vibrateForDragCommit(). */ public void vibrateForDragTexture() { if (mDragEffect == null) { @@ -145,7 +162,7 @@ public class VibratorWrapper { } /** - * This is used when user reaches the commit threshold when swiping to/from from all apps. + * This is used when user reaches the commit threshold when swiping to/from from all apps. */ public void vibrateForDragCommit() { if (mCommitEffect != null) { @@ -156,9 +173,9 @@ public class VibratorWrapper { } /** - * The bump haptic is used to be called at the end of a swipe and only if it the gesture is a - * FLING going to/from all apps. Client can just call this method elsewhere just for the - * effect. + * The bump haptic is used to be called at the end of a swipe and only if it the gesture is a + * FLING going to/from all apps. Client can just call this method elsewhere just for the + * effect. */ public void vibrateForDragBump() { if (mBumpEffect != null) { @@ -167,6 +184,15 @@ public class VibratorWrapper { } /** + * The assist haptic is used to be called when an assistant is invoked + */ + public void vibrateForAssist() { + if (mAssistEffect != null) { + vibrate(mAssistEffect); + } + } + + /** * This should be used to cancel a haptic in case where the haptic shouldn't be vibrating. For * example, when no animation is happening but a vibrator happens to be vibrating still. Need * boolean parameter for {@link PendingAnimation#addEndListener(Consumer)}. @@ -176,6 +202,7 @@ public class VibratorWrapper { // reset dragTexture timestamp to be able to play dragTexture again whenever cancelled mLastDragTime = 0; } + private boolean isHapticFeedbackEnabled(ContentResolver resolver) { return Settings.System.getInt(resolver, HAPTIC_FEEDBACK_ENABLED, 0) == 1; } diff --git a/src/com/android/launcher3/util/WallpaperColorHints.kt b/src/com/android/launcher3/util/WallpaperColorHints.kt new file mode 100644 index 0000000000..1361c1ed21 --- /dev/null +++ b/src/com/android/launcher3/util/WallpaperColorHints.kt @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.util + +import android.app.WallpaperColors +import android.app.WallpaperManager +import android.app.WallpaperManager.FLAG_SYSTEM +import android.app.WallpaperManager.OnColorsChangedListener +import android.content.Context +import androidx.annotation.MainThread +import androidx.annotation.VisibleForTesting +import com.android.launcher3.Utilities +import com.android.launcher3.util.Executors.MAIN_EXECUTOR +import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR + +/** + * This class caches the system's wallpaper color hints for use by other classes as a performance + * enhancer. It also centralizes all the WallpaperManager color hint code in one location. + */ +class WallpaperColorHints(private val context: Context) : SafeCloseable { + var hints: Int = 0 + private set + private val wallpaperManager + get() = context.getSystemService(WallpaperManager::class.java)!! + private val onColorHintsChangedListeners = mutableListOf<OnColorHintListener>() + private val onClose: SafeCloseable + + init { + if (Utilities.ATLEAST_S) { + hints = wallpaperManager.getWallpaperColors(FLAG_SYSTEM)?.colorHints ?: 0 + val onColorsChangedListener = OnColorsChangedListener { colors, which -> + onColorsChanged(colors, which) + } + UI_HELPER_EXECUTOR.execute { + wallpaperManager.addOnColorsChangedListener( + onColorsChangedListener, + MAIN_EXECUTOR.handler + ) + } + onClose = SafeCloseable { + UI_HELPER_EXECUTOR.execute { + wallpaperManager.removeOnColorsChangedListener(onColorsChangedListener) + } + } + } else { + onClose = SafeCloseable {} + } + } + + @MainThread + private fun onColorsChanged(colors: WallpaperColors?, which: Int) { + if ((which and FLAG_SYSTEM) != 0 && Utilities.ATLEAST_S) { + val newHints = colors?.colorHints ?: 0 + if (newHints != hints) { + hints = newHints + onColorHintsChangedListeners.forEach { it.onColorHintsChanged(newHints) } + } + } + } + + override fun close() = onClose.close() + + fun registerOnColorHintsChangedListener(listener: OnColorHintListener) { + onColorHintsChangedListeners.add(listener) + } + + fun unregisterOnColorsChangedListener(listener: OnColorHintListener) { + onColorHintsChangedListeners.remove(listener) + } + + companion object { + @VisibleForTesting + @JvmField + val INSTANCE = MainThreadInitializedObject { WallpaperColorHints(it) } + @JvmStatic fun get(context: Context): WallpaperColorHints = INSTANCE.get(context) + } +} + +interface OnColorHintListener { + fun onColorHintsChanged(colorHints: Int) +} diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java index 4ac6bc475c..b97b8894c7 100644 --- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java +++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java @@ -15,9 +15,9 @@ import android.view.animation.Interpolator; import androidx.annotation.AnyThread; +import com.android.app.animation.Interpolators; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; -import com.android.launcher3.anim.Interpolators; /** * Utility class to handle wallpaper scrolling along with workspace. @@ -237,7 +237,7 @@ public class WallpaperOffsetInterpolator { public OffsetHandler(Context context) { super(UI_HELPER_EXECUTOR.getLooper()); - mInterpolator = Interpolators.DEACCEL_1_5; + mInterpolator = Interpolators.DECELERATE_1_5; mWM = WallpaperManager.getInstance(context); } diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java index 4093bc913d..278a37e353 100644 --- a/src/com/android/launcher3/util/window/WindowManagerProxy.java +++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java @@ -57,6 +57,9 @@ import com.android.launcher3.util.NavigationMode; import com.android.launcher3.util.ResourceBasedOverride; import com.android.launcher3.util.WindowBounds; +import java.util.ArrayList; +import java.util.List; + /** * Utility class for mocking some window manager behaviours */ @@ -90,11 +93,11 @@ public class WindowManagerProxy implements ResourceBasedOverride { * Returns a map of normalized info of internal displays to estimated window bounds * for that display */ - public ArrayMap<CachedDisplayInfo, WindowBounds[]> estimateInternalDisplayBounds( + public ArrayMap<CachedDisplayInfo, List<WindowBounds>> estimateInternalDisplayBounds( Context displayInfoContext) { CachedDisplayInfo info = getDisplayInfo(displayInfoContext).normalize(); - WindowBounds[] bounds = estimateWindowBounds(displayInfoContext, info); - ArrayMap<CachedDisplayInfo, WindowBounds[]> result = new ArrayMap<>(); + List<WindowBounds> bounds = estimateWindowBounds(displayInfoContext, info); + ArrayMap<CachedDisplayInfo, List<WindowBounds>> result = new ArrayMap<>(); result.put(info, bounds); return result; } @@ -200,7 +203,8 @@ public class WindowManagerProxy implements ResourceBasedOverride { /** * Returns a list of possible WindowBounds for the display keyed on the 4 surface rotations */ - protected WindowBounds[] estimateWindowBounds(Context context, CachedDisplayInfo displayInfo) { + protected List<WindowBounds> estimateWindowBounds(Context context, + CachedDisplayInfo displayInfo) { int densityDpi = context.getResources().getConfiguration().densityDpi; int rotation = displayInfo.rotation; Rect safeCutout = displayInfo.cutout; @@ -243,7 +247,7 @@ public class WindowManagerProxy implements ResourceBasedOverride { ? 0 : getDimenByName(systemRes, NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE); - WindowBounds[] result = new WindowBounds[4]; + List<WindowBounds> result = new ArrayList<>(4); Point tempSize = new Point(); for (int i = 0; i < 4; i++) { int rotationChange = deltaRotation(rotation, i); @@ -274,7 +278,7 @@ public class WindowManagerProxy implements ResourceBasedOverride { } else { insets.right = Math.max(insets.right, navbarWidth); } - result[i] = new WindowBounds(bounds, insets, i); + result.add(new WindowBounds(bounds, insets, i)); } return result; } diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java index ec7ec0b6c0..30e09718c2 100644 --- a/src/com/android/launcher3/views/AbstractSlideInView.java +++ b/src/com/android/launcher3/views/AbstractSlideInView.java @@ -17,24 +17,22 @@ package com.android.launcher3.views; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.LauncherAnimUtils.TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.allapps.AllAppsTransitionController.REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS; -import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; +import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Outline; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; -import android.util.Property; +import android.util.FloatProperty; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -47,10 +45,13 @@ import androidx.annotation.Nullable; import androidx.annotation.Px; import androidx.annotation.RequiresApi; +import com.android.app.animation.Interpolators; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatedFloat; -import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.anim.AnimatorListeners; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.touch.BaseSwipeDetector; import com.android.launcher3.touch.SingleAxisSwipeDetector; @@ -66,8 +67,8 @@ import java.util.Optional; public abstract class AbstractSlideInView<T extends Context & ActivityContext> extends AbstractFloatingView implements SingleAxisSwipeDetector.Listener { - protected static final Property<AbstractSlideInView, Float> TRANSLATION_SHIFT = - new Property<AbstractSlideInView, Float>(Float.class, "translationShift") { + protected static final FloatProperty<AbstractSlideInView<?>> TRANSLATION_SHIFT = + new FloatProperty<>("translationShift") { @Override public Float get(AbstractSlideInView view) { @@ -75,25 +76,54 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext> } @Override - public void set(AbstractSlideInView view, Float value) { + public void setValue(AbstractSlideInView view, float value) { view.setTranslationShift(value); } }; protected static final float TRANSLATION_SHIFT_CLOSED = 1f; protected static final float TRANSLATION_SHIFT_OPENED = 0f; private static final float VIEW_NO_SCALE = 1f; + private static final int DEFAULT_DURATION = 300; protected final T mActivityContext; protected final SingleAxisSwipeDetector mSwipeDetector; - protected final ObjectAnimator mOpenCloseAnimator; + protected @NonNull AnimatorPlaybackController mOpenCloseAnimation; protected ViewGroup mContent; protected final View mColorScrim; - protected Interpolator mScrollInterpolator; + + /** + * Interpolator for {@link #mOpenCloseAnimation} when we are closing due to dragging downwards. + */ + private Interpolator mScrollInterpolator; + private long mScrollDuration; + /** + * End progress for {@link #mOpenCloseAnimation} when we are closing due to dragging downloads. + * <p> + * There are two cases that determine this value: + * <ol> + * <li> + * If the drag interrupts the opening transition (i.e. {@link #mToTranslationShift} + * is {@link #TRANSLATION_SHIFT_OPENED}), we need to animate back to {@code 0} to + * reverse the animation that was paused at {@link #onDragStart(boolean, float)}. + * </li> + * <li> + * If the drag started after the view is fully opened (i.e. + * {@link #mToTranslationShift} is {@link #TRANSLATION_SHIFT_CLOSED}), the animation + * that was set up at {@link #onDragStart(boolean, float)} for closing the view + * should go forward to {@code 1}. + * </li> + * </ol> + */ + private float mScrollEndProgress; // range [0, 1], 0=> completely open, 1=> completely closed protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED; + protected float mFromTranslationShift; + protected float mToTranslationShift; + /** {@link #mOpenCloseAnimation} progress at {@link #onDragStart(boolean, float)}. */ + private float mDragStartProgress; protected boolean mNoIntercept; protected @Nullable OnCloseListener mOnCloseBeginListener; @@ -102,8 +132,8 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext> protected final AnimatedFloat mSlideInViewScale = new AnimatedFloat(this::onScaleProgressChanged, VIEW_NO_SCALE); protected boolean mIsBackProgressing; - @Nullable private Drawable mContentBackground; - @Nullable private View mContentBackgroundParentView; + private @Nullable Drawable mContentBackground; + private @Nullable View mContentBackgroundParentView; protected final ViewOutlineProvider mViewOutlineProvider = new ViewOutlineProvider() { @Override @@ -122,21 +152,78 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext> mActivityContext = ActivityContext.lookupContext(context); mScrollInterpolator = Interpolators.SCROLL_CUBIC; + mScrollDuration = DEFAULT_DURATION; mSwipeDetector = new SingleAxisSwipeDetector(context, this, SingleAxisSwipeDetector.VERTICAL); - mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this); - mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mSwipeDetector.finishedScrolling(); - announceAccessibilityChanges(); - } - }); + mOpenCloseAnimation = new PendingAnimation(0).createPlaybackController(); + int scrimColor = getScrimColor(context); mColorScrim = scrimColor != -1 ? createColorScrim(context, scrimColor) : null; } + /** + * Sets up a {@link #mOpenCloseAnimation} for opening with default parameters. + * + * @see #setUpOpenCloseAnimation(float, float, long) + */ + protected final AnimatorPlaybackController setUpDefaultOpenAnimation() { + AnimatorPlaybackController animation = setUpOpenCloseAnimation( + TRANSLATION_SHIFT_CLOSED, TRANSLATION_SHIFT_OPENED, DEFAULT_DURATION); + animation.getAnimationPlayer().setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + return animation; + } + + /** + * Sets up a {@link #mOpenCloseAnimation} for opening with a given duration. + * + * @see #setUpOpenCloseAnimation(float, float, long) + */ + protected final AnimatorPlaybackController setUpOpenAnimation(long duration) { + return setUpOpenCloseAnimation( + TRANSLATION_SHIFT_CLOSED, TRANSLATION_SHIFT_OPENED, duration); + } + + private AnimatorPlaybackController setUpCloseAnimation(long duration) { + return setUpOpenCloseAnimation( + TRANSLATION_SHIFT_OPENED, TRANSLATION_SHIFT_CLOSED, duration); + } + + /** + * Initializes a new {@link #mOpenCloseAnimation}. + * + * @param fromTranslationShift translation shift to animate from. + * @param toTranslationShift translation shift to animate to. + * @param duration animation duration. + * @return {@link #mOpenCloseAnimation} + */ + private AnimatorPlaybackController setUpOpenCloseAnimation( + float fromTranslationShift, float toTranslationShift, long duration) { + mFromTranslationShift = fromTranslationShift; + mToTranslationShift = toTranslationShift; + + PendingAnimation animation = new PendingAnimation(duration); + animation.addEndListener(b -> { + mSwipeDetector.finishedScrolling(); + announceAccessibilityChanges(); + }); + + animation.addFloat( + this, TRANSLATION_SHIFT, fromTranslationShift, toTranslationShift, LINEAR); + onOpenCloseAnimationPending(animation); + + mOpenCloseAnimation = animation.createPlaybackController(); + return mOpenCloseAnimation; + } + + /** + * Invoked when a {@link #mOpenCloseAnimation} is being set up. + * <p> + * Subclasses can override this method to modify the animation before it's used to create a + * {@link AnimatorPlaybackController}. + */ + protected void onOpenCloseAnimationPending(PendingAnimation animation) {} + protected void attachToContainer() { if (mColorScrim != null) { getPopupContainer().addView(mColorScrim); @@ -279,19 +366,28 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext> } private boolean isOpeningAnimationRunning() { - return mIsOpen && mOpenCloseAnimator.isRunning(); + return mIsOpen && mOpenCloseAnimation.getAnimationPlayer().isRunning(); } /* SingleAxisSwipeDetector.Listener */ @Override - public void onDragStart(boolean start, float startDisplacement) { } + public void onDragStart(boolean start, float startDisplacement) { + if (mOpenCloseAnimation.getAnimationPlayer().isRunning()) { + mOpenCloseAnimation.pause(); + mDragStartProgress = mOpenCloseAnimation.getProgressFraction(); + } else { + setUpCloseAnimation(DEFAULT_DURATION); + mDragStartProgress = 0; + } + } @Override public boolean onDrag(float displacement) { - float range = getShiftRange(); - displacement = Utilities.boundToRange(displacement, 0, range); - setTranslationShift(displacement / range); + float progress = mDragStartProgress + + Math.signum(mToTranslationShift - mFromTranslationShift) + * (displacement / getShiftRange()); + mOpenCloseAnimation.setPlayFraction(Utilities.boundToRange(progress, 0, 1)); return true; } @@ -302,16 +398,18 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext> if ((mSwipeDetector.isFling(velocity) && velocity > 0) || mTranslationShift > successfulShiftThreshold) { mScrollInterpolator = scrollInterpolatorForVelocity(velocity); - mOpenCloseAnimator.setDuration(BaseSwipeDetector.calculateDuration( - velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift)); + mScrollDuration = BaseSwipeDetector.calculateDuration( + velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift); + mScrollEndProgress = mToTranslationShift == TRANSLATION_SHIFT_OPENED ? 0 : 1; close(true); } else { - mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat( - TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); - mOpenCloseAnimator.setDuration( - BaseSwipeDetector.calculateDuration(velocity, mTranslationShift)) - .setInterpolator(Interpolators.DEACCEL); - mOpenCloseAnimator.start(); + ValueAnimator animator = mOpenCloseAnimation.getAnimationPlayer(); + animator.setInterpolator(Interpolators.DECELERATE); + animator.setFloatValues( + mOpenCloseAnimation.getProgressFraction(), + mToTranslationShift == TRANSLATION_SHIFT_OPENED ? 1 : 0); + animator.setDuration(BaseSwipeDetector.calculateDuration(velocity, mTranslationShift)) + .start(); } } @@ -332,32 +430,31 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext> Optional.ofNullable(mOnCloseBeginListener).ifPresent(OnCloseListener::onSlideInViewClosed); if (!animate) { - mOpenCloseAnimator.cancel(); + mOpenCloseAnimation.pause(); setTranslationShift(TRANSLATION_SHIFT_CLOSED); onCloseComplete(); return; } - mOpenCloseAnimator.setValues( - PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_CLOSED)); - mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mOpenCloseAnimator.removeListener(this); - onCloseComplete(); - } - }); + + final ValueAnimator animator; if (mSwipeDetector.isIdleState()) { - mOpenCloseAnimator - .setDuration(defaultDuration) - .setInterpolator(getIdleInterpolator()); + setUpCloseAnimation(defaultDuration); + animator = mOpenCloseAnimation.getAnimationPlayer(); + animator.setInterpolator(getIdleInterpolator()); } else { - mOpenCloseAnimator.setInterpolator(mScrollInterpolator); + animator = mOpenCloseAnimation.getAnimationPlayer(); + animator.setInterpolator(mScrollInterpolator); + animator.setDuration(mScrollDuration); + mOpenCloseAnimation.getAnimationPlayer().setFloatValues( + mOpenCloseAnimation.getProgressFraction(), mScrollEndProgress); } - mOpenCloseAnimator.start(); + + animator.addListener(AnimatorListeners.forEndCallback(this::onCloseComplete)); + animator.start(); } protected Interpolator getIdleInterpolator() { - return Interpolators.ACCEL; + return Interpolators.ACCELERATE; } protected void onCloseComplete() { diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index 4b319e5b61..84ea87118c 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -18,6 +18,7 @@ package com.android.launcher3.views; import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR; import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON; +import static com.android.launcher3.Utilities.allowBGLaunch; import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_PENDING_INTENT; @@ -38,7 +39,6 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.IBinder; import android.os.Process; -import android.os.StrictMode; import android.os.UserHandle; import android.util.Log; import android.view.Display; @@ -239,6 +239,11 @@ public interface ActivityContext { }; } + /** Long-click callback used for All Apps items. */ + default View.OnLongClickListener getAllAppsItemLongClickListener() { + return v -> false; + } + @Nullable default PopupDataProvider getPopupDataProvider() { return null; @@ -338,6 +343,12 @@ public interface ActivityContext { return null; } + boolean isShortcut = (item instanceof WorkspaceItemInfo) + && item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT + && !((WorkspaceItemInfo) item).isPromise(); + if (isShortcut && GO_DISABLE_WIDGETS) { + return null; + } ActivityOptionsWrapper options = v != null ? getActivityLaunchOptions(v, item) : makeDefaultActivityOptions(item != null && item.animationType == DEFAULT_NO_ICON ? SPLASH_SCREEN_STYLE_SOLID_COLOR : -1 /* SPLASH_SCREEN_STYLE_UNDEFINED */); @@ -349,13 +360,11 @@ public interface ActivityContext { intent.setSourceBounds(Utilities.getViewBounds(v)); } try { - boolean isShortcut = (item instanceof WorkspaceItemInfo) - && (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT - || item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) - && !((WorkspaceItemInfo) item).isPromise(); if (isShortcut) { - // Shortcuts need some special checks due to legacy reasons. - startShortcutIntentSafely(intent, optsBundle, item); + String id = ((WorkspaceItemInfo) item).getDeepShortcutId(); + String packageName = intent.getPackage(); + ((Context) this).getSystemService(LauncherApps.class).startShortcut( + packageName, id, intent.getSourceBounds(), optsBundle, user); } else if (user == null || user.equals(Process.myUserHandle())) { // Could be launching some bookkeeping activity context.startActivity(intent, optsBundle); @@ -410,8 +419,7 @@ public interface ActivityContext { } } ActivityOptions options = - ActivityOptions.makeClipRevealAnimation(v, left, top, width, height); - + allowBGLaunch(ActivityOptions.makeClipRevealAnimation(v, left, top, width, height)); options.setLaunchDisplayId( (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId() : Display.DEFAULT_DISPLAY); @@ -423,62 +431,13 @@ public interface ActivityContext { * Creates a default activity option and we do not want association with any launcher element. */ default ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) { - ActivityOptions options = ActivityOptions.makeBasic(); + ActivityOptions options = allowBGLaunch(ActivityOptions.makeBasic()); if (Utilities.ATLEAST_T) { options.setSplashScreenStyle(splashScreenStyle); } return new ActivityOptionsWrapper(options, new RunnableList()); } - /** - * Safely launches an intent for a shortcut. - * - * @param intent Intent to start. - * @param optsBundle Optional launch arguments. - * @param info Shortcut information. - */ - default void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) { - try { - StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy(); - try { - // Temporarily disable deathPenalty on all default checks. For eg, shortcuts - // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure - // is enabled by default on NYC. - StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll() - .penaltyLog().build()); - - if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - String id = ((WorkspaceItemInfo) info).getDeepShortcutId(); - String packageName = intent.getPackage(); - startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user); - } else { - // Could be launching some bookkeeping activity - ((Context) this).startActivity(intent, optsBundle); - } - } finally { - StrictMode.setVmPolicy(oldPolicy); - } - } catch (SecurityException e) { - throw e; - } - } - - /** - * A wrapper around the platform method with Launcher specific checks. - */ - default void startShortcut(String packageName, String id, Rect sourceBounds, - Bundle startActivityOptions, UserHandle user) { - if (GO_DISABLE_WIDGETS) { - return; - } - try { - ((Context) this).getSystemService(LauncherApps.class).startShortcut(packageName, id, - sourceBounds, startActivityOptions, user); - } catch (SecurityException | IllegalStateException e) { - Log.e(TAG, "Failed to start shortcut", e); - } - } - default CellPosMapper getCellPosMapper() { return CellPosMapper.DEFAULT; } diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java index 73c5ad457b..b44dbeb616 100644 --- a/src/com/android/launcher3/views/ArrowTipView.java +++ b/src/com/android/launcher3/views/ArrowTipView.java @@ -16,14 +16,21 @@ package com.android.launcher3.views; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Configuration; +import android.content.res.TypedArray; import android.graphics.CornerPathEffect; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.ShapeDrawable; import android.os.Handler; +import android.util.IntProperty; import android.util.Log; +import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; @@ -33,18 +40,17 @@ import android.widget.TextView; import androidx.annotation.Nullable; import androidx.annotation.Px; -import androidx.core.content.ContextCompat; +import com.android.app.animation.Interpolators; import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; -import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.anim.AnimatorListeners; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.graphics.TriangleShape; /** - * A base class for arrow tip view in launcher + * A base class for arrow tip view in launcher. */ public class ArrowTipView extends AbstractFloatingView { @@ -54,33 +60,66 @@ public class ArrowTipView extends AbstractFloatingView { private static final long SHOW_DURATION_MS = 300; private static final long HIDE_DURATION_MS = 100; - protected final BaseDraggingActivity mActivity; + public static final IntProperty<ArrowTipView> TEXT_ALPHA = + new IntProperty<>("textAlpha") { + @Override + public void setValue(ArrowTipView view, int v) { + view.setTextAlpha(v); + } + + @Override + public Integer get(ArrowTipView view) { + return view.getTextAlpha(); + } + }; + + private final ActivityContext mActivityContext; private final Handler mHandler = new Handler(); - private final int mArrowWidth; - private final int mArrowMinOffset; private boolean mIsPointingUp; private Runnable mOnClosed; private View mArrowView; + private final int mArrowWidth; + private final int mArrowMinOffset; + private final int mArrowViewPaintColor; + + private AnimatorSet mOpenAnimator = new AnimatorSet(); + private AnimatorSet mCloseAnimator = new AnimatorSet(); + + private int mTextAlpha; public ArrowTipView(Context context) { this(context, false); } public ArrowTipView(Context context, boolean isPointingUp) { + this(context, isPointingUp, R.layout.arrow_toast); + } + + public ArrowTipView(Context context, boolean isPointingUp, int layoutId) { super(context, null, 0); - mActivity = BaseDraggingActivity.fromContext(context); + mActivityContext = ActivityContext.lookupContext(context); mIsPointingUp = isPointingUp; - mArrowWidth = context.getResources().getDimensionPixelSize(R.dimen.arrow_toast_arrow_width); + mArrowWidth = context.getResources().getDimensionPixelSize( + R.dimen.arrow_toast_arrow_width); mArrowMinOffset = context.getResources().getDimensionPixelSize( R.dimen.dynamic_grid_cell_border_spacing); - init(context); + TypedArray ta = context.obtainStyledAttributes(R.styleable.ArrowTipView); + // Set style to default to avoid inflation issues with missing attributes. + if (!ta.hasValue(R.styleable.ArrowTipView_arrowTipBackground) + || !ta.hasValue(R.styleable.ArrowTipView_arrowTipTextColor)) { + context = new ContextThemeWrapper(context, R.style.ArrowTipStyle); + } + mArrowViewPaintColor = ta.getColor(R.styleable.ArrowTipView_arrowTipBackground, + context.getColor(R.color.arrow_tip_view_bg)); + ta.recycle(); + init(context, layoutId); } @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { close(true); - if (mActivity.getDragLayer().isEventOverView(this, ev)) { + if (mActivityContext.getDragLayer().isEventOverView(this, ev)) { return true; } } @@ -89,18 +128,17 @@ public class ArrowTipView extends AbstractFloatingView { @Override protected void handleClose(boolean animate) { + if (mOpenAnimator.isStarted()) { + mOpenAnimator.cancel(); + } if (mIsOpen) { if (animate) { - animate().alpha(0f) - .withLayer() - .setStartDelay(0) - .setDuration(HIDE_DURATION_MS) - .setInterpolator(Interpolators.ACCEL) - .withEndAction(() -> mActivity.getDragLayer().removeView(this)) - .start(); + mCloseAnimator.addListener(AnimatorListeners.forSuccessCallback( + () -> mActivityContext.getDragLayer().removeView(this))); + mCloseAnimator.start(); } else { - animate().cancel(); - mActivity.getDragLayer().removeView(this); + mCloseAnimator.cancel(); + mActivityContext.getDragLayer().removeView(this); } if (mOnClosed != null) mOnClosed.run(); mIsOpen = false; @@ -112,12 +150,31 @@ public class ArrowTipView extends AbstractFloatingView { return (type & TYPE_ON_BOARD_POPUP) != 0; } - private void init(Context context) { - inflate(context, R.layout.arrow_toast, this); + private void init(Context context, int layoutId) { + inflate(context, layoutId, this); setOrientation(LinearLayout.VERTICAL); mArrowView = findViewById(R.id.arrow); updateArrowTipInView(); + setAlpha(0); + + // Create default open animator. + mOpenAnimator.play(ObjectAnimator.ofFloat(this, ALPHA, 1f)); + mOpenAnimator.setStartDelay(SHOW_DELAY_MS); + mOpenAnimator.setDuration(SHOW_DURATION_MS); + mOpenAnimator.setInterpolator(Interpolators.DECELERATE); + + // Create default close animator. + mCloseAnimator.play(ObjectAnimator.ofFloat(this, ALPHA, 0)); + mCloseAnimator.setStartDelay(0); + mCloseAnimator.setDuration(HIDE_DURATION_MS); + mCloseAnimator.setInterpolator(Interpolators.ACCELERATE); + mCloseAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mActivityContext.getDragLayer().removeView(ArrowTipView.this); + } + }); } /** @@ -153,10 +210,10 @@ public class ArrowTipView extends AbstractFloatingView { public ArrowTipView show( String text, int gravity, int arrowMarginStart, int top, boolean shouldAutoClose) { ((TextView) findViewById(R.id.text)).setText(text); - ViewGroup parent = mActivity.getDragLayer(); + ViewGroup parent = mActivityContext.getDragLayer(); parent.addView(this); - DeviceProfile grid = mActivity.getDeviceProfile(); + DeviceProfile grid = mActivityContext.getDeviceProfile(); DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams(); params.gravity = gravity; @@ -185,14 +242,8 @@ public class ArrowTipView extends AbstractFloatingView { if (shouldAutoClose) { mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS); } - setAlpha(0); - animate() - .alpha(1f) - .withLayer() - .setStartDelay(SHOW_DELAY_MS) - .setDuration(SHOW_DURATION_MS) - .setInterpolator(Interpolators.DEACCEL) - .start(); + + mOpenAnimator.start(); return this; } @@ -273,7 +324,7 @@ public class ArrowTipView extends AbstractFloatingView { */ @Nullable private ArrowTipView showAtLocation(String text, @Px int arrowXCoord, @Px int yCoordDownPointingTip, @Px int yCoordUpPointingTip, boolean shouldAutoClose) { - ViewGroup parent = mActivity.getDragLayer(); + ViewGroup parent = mActivityContext.getDragLayer(); @Px int parentViewWidth = parent.getWidth(); @Px int parentViewHeight = parent.getHeight(); @Px int maxTextViewWidth = getContext().getResources() @@ -288,8 +339,10 @@ public class ArrowTipView extends AbstractFloatingView { TextView textView = findViewById(R.id.text); textView.setText(text); textView.setMaxWidth(maxTextViewWidth); - parent.addView(this); - requestLayout(); + if (parent.indexOfChild(this) < 0) { + parent.addView(this); + requestLayout(); + } post(() -> { // Adjust the tooltip horizontally. @@ -333,14 +386,8 @@ public class ArrowTipView extends AbstractFloatingView { if (shouldAutoClose) { mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS); } - setAlpha(0); - animate() - .alpha(1f) - .withLayer() - .setStartDelay(SHOW_DELAY_MS) - .setDuration(SHOW_DURATION_MS) - .setInterpolator(Interpolators.DEACCEL) - .start(); + + mOpenAnimator.start(); return this; } @@ -351,7 +398,7 @@ public class ArrowTipView extends AbstractFloatingView { Paint arrowPaint = arrowDrawable.getPaint(); @Px int arrowTipRadius = getContext().getResources() .getDimensionPixelSize(R.dimen.arrow_toast_corner_radius); - arrowPaint.setColor(ContextCompat.getColor(getContext(), R.color.arrow_tip_view_bg)); + arrowPaint.setColor(mArrowViewPaintColor); arrowPaint.setPathEffect(new CornerPathEffect(arrowTipRadius)); mArrowView.setBackground(arrowDrawable); // Add negative margin so that the rounded corners on base of arrow are not visible. @@ -378,4 +425,30 @@ public class ArrowTipView extends AbstractFloatingView { super.onConfigurationChanged(newConfig); close(/* animate= */ false); } + + /** + * Sets a custom animation to run on open of the ArrowTipView. + */ + public void setCustomOpenAnimation(AnimatorSet animator) { + mOpenAnimator = animator; + } + + /** + * Sets a custom animation to run on close of the ArrowTipView. + */ + public void setCustomCloseAnimation(AnimatorSet animator) { + mCloseAnimator = animator; + } + + private void setTextAlpha(int textAlpha) { + if (mTextAlpha != textAlpha) { + mTextAlpha = textAlpha; + TextView textView = findViewById(R.id.text); + textView.setTextColor(textView.getTextColors().withAlpha(mTextAlpha)); + } + } + + private int getTextAlpha() { + return mTextAlpha; + } } diff --git a/src/com/android/launcher3/views/ClipIconView.java b/src/com/android/launcher3/views/ClipIconView.java index 694deadb63..87e496e4c2 100644 --- a/src/com/android/launcher3/views/ClipIconView.java +++ b/src/com/android/launcher3/views/ClipIconView.java @@ -15,9 +15,9 @@ */ package com.android.launcher3.views; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.Utilities.boundToRange; import static com.android.launcher3.Utilities.mapToRange; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; import static java.lang.Math.max; diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 6b5c8df954..41b98c74a6 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -17,10 +17,10 @@ package com.android.launcher3.views; import static android.view.Gravity.LEFT; +import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.Utilities.getBadge; import static com.android.launcher3.Utilities.getFullDrawable; import static com.android.launcher3.Utilities.mapToRange; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible; @@ -289,12 +289,14 @@ public class FloatingIconView extends FrameLayout implements int width = (int) pos.width(); int height = (int) pos.height(); Object[] tmpObjArray = new Object[1]; + boolean[] outIsIconThemed = new boolean[1]; if (supportsAdaptiveIcons) { boolean shouldThemeIcon = btvIcon instanceof FastBitmapDrawable && ((FastBitmapDrawable) btvIcon).isThemed(); - drawable = getFullDrawable(l, info, width, height, shouldThemeIcon, tmpObjArray); + drawable = getFullDrawable( + l, info, width, height, shouldThemeIcon, tmpObjArray, outIsIconThemed); if (drawable instanceof AdaptiveIconDrawable) { - badge = getBadge(l, info, tmpObjArray[0]); + badge = getBadge(l, info, tmpObjArray[0], outIsIconThemed[0]); } else { // The drawable we get back is not an adaptive icon, so we need to use the // BubbleTextView icon that is already legacy treated. @@ -306,7 +308,7 @@ public class FloatingIconView extends FrameLayout implements drawable = btvIcon; } else { drawable = getFullDrawable(l, info, width, height, true /* shouldThemeIcon */, - tmpObjArray); + tmpObjArray, outIsIconThemed); } } } diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index 4641e31b08..b62f60dc12 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -55,7 +55,6 @@ import com.android.launcher3.popup.ArrowPopup; import com.android.launcher3.shortcuts.DeepShortcutView; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; -import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.widget.picker.WidgetsFullSheet; import java.util.ArrayList; @@ -63,8 +62,10 @@ import java.util.List; /** * Popup shown on long pressing an empty space in launcher + * + * @param <T> The context showing this popup. */ -public class OptionsPopupView extends ArrowPopup<Launcher> +public class OptionsPopupView<T extends Context & ActivityContext> extends ArrowPopup<T> implements OnClickListener, OnLongClickListener { // An intent extra to indicate the horizontal scroll of the wallpaper. @@ -156,21 +157,27 @@ public class OptionsPopupView extends ArrowPopup<Launcher> } } - public static OptionsPopupView show(ActivityContext launcher, RectF targetRect, - List<OptionItem> items, boolean shouldAddArrow) { - return show(launcher, targetRect, items, shouldAddArrow, 0 /* width */); + public static <T extends Context & ActivityContext> OptionsPopupView<T> show( + ActivityContext activityContext, + RectF targetRect, + List<OptionItem> items, + boolean shouldAddArrow) { + return show(activityContext, targetRect, items, shouldAddArrow, 0 /* width */); } - public static OptionsPopupView show(ActivityContext launcher, RectF targetRect, - List<OptionItem> items, boolean shouldAddArrow, int width) { - OptionsPopupView popup = (OptionsPopupView) launcher.getLayoutInflater() - .inflate(R.layout.longpress_options_menu, launcher.getDragLayer(), false); + public static <T extends Context & ActivityContext> OptionsPopupView<T> show( + ActivityContext activityContext, + RectF targetRect, + List<OptionItem> items, + boolean shouldAddArrow, + int width) { + OptionsPopupView<T> popup = (OptionsPopupView<T>) activityContext.getLayoutInflater() + .inflate(R.layout.longpress_options_menu, activityContext.getDragLayer(), false); popup.mTargetRect = targetRect; popup.setShouldAddArrow(shouldAddArrow); for (OptionItem item : items) { - DeepShortcutView view = - (DeepShortcutView) popup.inflateAndAdd(R.layout.system_shortcut, popup); + DeepShortcutView view = popup.inflateAndAdd(R.layout.system_shortcut, popup); if (width > 0) { view.getLayoutParams().width = width; } @@ -190,14 +197,9 @@ public class OptionsPopupView extends ArrowPopup<Launcher> */ public static ArrayList<OptionItem> getOptions(Launcher launcher) { ArrayList<OptionItem> options = new ArrayList<>(); - boolean styleWallpaperExists = styleWallpapersExists(launcher); - int resString = styleWallpaperExists - ? R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text; - int resDrawable = styleWallpaperExists - ? R.drawable.ic_palette : R.drawable.ic_wallpaper; options.add(new OptionItem(launcher, - resString, - resDrawable, + R.string.styles_wallpaper_button_text, + R.drawable.ic_palette, IGNORE, OptionsPopupView::startWallpaperPicker)); if (!WidgetsModel.GO_DISABLE_WIDGETS) { @@ -274,12 +276,8 @@ public class OptionsPopupView extends ArrowPopup<Launcher> .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) .putExtra(EXTRA_WALLPAPER_OFFSET, launcher.getWorkspace().getWallpaperOffsetForCenterPage()) - .putExtra(EXTRA_WALLPAPER_LAUNCH_SOURCE, "app_launched_launcher"); - if (!styleWallpapersExists(launcher)) { - intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only"); - } else { - intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "focus_wallpaper"); - } + .putExtra(EXTRA_WALLPAPER_LAUNCH_SOURCE, "app_launched_launcher") + .putExtra(EXTRA_WALLPAPER_FLAVOR, "focus_wallpaper"); String pickerPackage = launcher.getString(R.string.wallpaper_picker_package); if (!TextUtils.isEmpty(pickerPackage)) { intent.setPackage(pickerPackage); @@ -290,7 +288,6 @@ public class OptionsPopupView extends ArrowPopup<Launcher> static WorkspaceItemInfo placeholderInfo(Intent intent) { WorkspaceItemInfo placeholderInfo = new WorkspaceItemInfo(); placeholderInfo.intent = intent; - placeholderInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; placeholderInfo.container = LauncherSettings.Favorites.CONTAINER_SETTINGS; return placeholderInfo; } @@ -323,9 +320,4 @@ public class OptionsPopupView extends ArrowPopup<Launcher> this.clickListener = clickListener; } } - - private static boolean styleWallpapersExists(Context context) { - return context.getPackageManager().resolveActivity( - PackageManagerHelper.getStyleWallpapersIntent(context), 0) != null; - } } diff --git a/src/com/android/launcher3/views/Snackbar.java b/src/com/android/launcher3/views/Snackbar.java index 2460be17c5..99040ff845 100644 --- a/src/com/android/launcher3/views/Snackbar.java +++ b/src/com/android/launcher3/views/Snackbar.java @@ -30,10 +30,10 @@ import android.widget.TextView; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.dragndrop.DragLayer; @@ -175,7 +175,7 @@ public class Snackbar extends AbstractFloatingView { .scaleX(1) .scaleY(1) .setDuration(SHOW_DURATION_MS) - .setInterpolator(Interpolators.ACCEL_DEACCEL) + .setInterpolator(Interpolators.ACCELERATE_DECELERATE) .start(); int timeout = AccessibilityManagerCompat.getRecommendedTimeoutMillis(activity, TIMEOUT_DURATION_MS, FLAG_CONTENT_TEXT | FLAG_CONTENT_CONTROLS); @@ -190,7 +190,7 @@ public class Snackbar extends AbstractFloatingView { .withLayer() .setStartDelay(0) .setDuration(HIDE_DURATION_MS) - .setInterpolator(Interpolators.ACCEL) + .setInterpolator(Interpolators.ACCELERATE) .withEndAction(this::onClosed) .start(); } else { diff --git a/src/com/android/launcher3/views/WidgetsEduView.java b/src/com/android/launcher3/views/WidgetsEduView.java index c2947c7310..e70b1cbd16 100644 --- a/src/com/android/launcher3/views/WidgetsEduView.java +++ b/src/com/android/launcher3/views/WidgetsEduView.java @@ -15,9 +15,6 @@ */ package com.android.launcher3.views; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; - -import android.animation.PropertyValuesHolder; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; @@ -119,14 +116,11 @@ public class WidgetsEduView extends AbstractSlideInView<Launcher> implements Ins } private void animateOpen() { - if (mIsOpen || mOpenCloseAnimator.isRunning()) { + if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) { return; } mIsOpen = true; - mOpenCloseAnimator.setValues( - PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); - mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN); - mOpenCloseAnimator.start(); + setUpDefaultOpenAnimation().start(); } /** Shows widget education dialog. */ diff --git a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java index 9442734646..80b1cdd83c 100644 --- a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java @@ -17,9 +17,7 @@ package com.android.launcher3.widget; import static com.android.launcher3.Utilities.ATLEAST_R; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; -import android.animation.PropertyValuesHolder; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Insets; @@ -130,14 +128,11 @@ public class AddItemWidgetsBottomSheet extends AbstractSlideInView<AddItemActivi } private void animateOpen() { - if (mIsOpen || mOpenCloseAnimator.isRunning()) { + if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) { return; } mIsOpen = true; - mOpenCloseAnimator.setValues( - PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); - mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN); - mOpenCloseAnimator.start(); + setUpDefaultOpenAnimation().start(); } @Override diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java index 049131ef10..dcc86a15e9 100644 --- a/src/com/android/launcher3/widget/BaseWidgetSheet.java +++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java @@ -15,7 +15,7 @@ */ package com.android.launcher3.widget; -import static com.android.launcher3.anim.Interpolators.EMPHASIZED; +import static com.android.app.animation.Interpolators.EMPHASIZED; import static com.android.launcher3.config.FeatureFlags.LARGE_SCREEN_WIDGET_PICKER; import android.content.Context; diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java index bc3889fd26..340a61e1c5 100644 --- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java @@ -95,6 +95,8 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView private boolean mTrackingWidgetUpdate = false; + private boolean mIsWidgetCachingDisabled = false; + public LauncherAppWidgetHostView(Context context) { super(context); mLauncher = Launcher.getLauncher(context); @@ -105,7 +107,7 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView if (Utilities.ATLEAST_Q && Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) { setOnLightBackground(true); } - mColorExtractor = LocalColorExtractor.newInstance(getContext()); + mColorExtractor = new LocalColorExtractor(); // no-op } @Override @@ -138,6 +140,10 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView } } + public void setIsWidgetCachingDisabled(boolean isWidgetCachingDisabled) { + mIsWidgetCachingDisabled = isWidgetCachingDisabled; + } + @Override @TargetApi(Build.VERSION_CODES.Q) public void updateAppWidget(RemoteViews remoteViews) { @@ -147,7 +153,8 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView TRACE_METHOD_NAME + getAppWidgetInfo().provider, getAppWidgetId()); mTrackingWidgetUpdate = false; } - if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) { + if (FeatureFlags.ENABLE_CACHED_WIDGET.get() + && !mIsWidgetCachingDisabled) { mLastRemoteViews = remoteViews; if (isDeferringUpdates()) { return; diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java index 2ca825cf6b..6acc83d1d1 100644 --- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java +++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java @@ -275,9 +275,15 @@ public class LauncherWidgetHolder { protected Bundle getConfigurationActivityOptions(@NonNull BaseDraggingActivity activity, int widgetId) { LauncherAppWidgetHostView view = mViews.get(widgetId); - if (view == null) return null; + if (view == null) { + return activity.makeDefaultActivityOptions( + -1 /* SPLASH_SCREEN_STYLE_UNDEFINED */).toBundle(); + } Object tag = view.getTag(); - if (!(tag instanceof ItemInfo)) return null; + if (!(tag instanceof ItemInfo)) { + return activity.makeDefaultActivityOptions( + -1 /* SPLASH_SCREEN_STYLE_UNDEFINED */).toBundle(); + } Bundle bundle = activity.getActivityLaunchOptions(view, (ItemInfo) tag).toBundle(); bundle.putInt(KEY_SPLASH_SCREEN_STYLE, SPLASH_SCREEN_STYLE_EMPTY); return bundle; diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index 846dafdf30..c347939d0a 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -17,9 +17,7 @@ package com.android.launcher3.widget; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_BOTTOM_WIDGETS_TRAY; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; -import android.animation.PropertyValuesHolder; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; @@ -226,15 +224,12 @@ public class WidgetsBottomSheet extends BaseWidgetSheet { } private void animateOpen() { - if (mIsOpen || mOpenCloseAnimator.isRunning()) { + if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) { return; } mIsOpen = true; setupNavBarColor(); - mOpenCloseAnimator.setValues( - PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); - mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN); - mOpenCloseAnimator.start(); + setUpDefaultOpenAnimation().start(); } @Override diff --git a/src/com/android/launcher3/widget/picker/OWNERS b/src/com/android/launcher3/widget/picker/OWNERS new file mode 100644 index 0000000000..6aabbfa119 --- /dev/null +++ b/src/com/android/launcher3/widget/picker/OWNERS @@ -0,0 +1,16 @@ +set noparent + +# Bug component: 1481801 + +# People who can approve changes for submission +# + +# Widget Picker OWNERS +zakcohen@google.com +shamalip@google.com +wvk@google.com + +# Launcher OWNERS +captaincole@google.com +sunnygoyal@google.com + diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index 33c4f8d7e2..a4b605cccd 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -23,8 +23,6 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL; import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.PropertyValuesHolder; import android.content.Context; import android.content.pm.LauncherApps; import android.content.res.Configuration; @@ -627,20 +625,13 @@ public class WidgetsFullSheet extends BaseWidgetSheet mContent.setAlpha(0); setTranslationShift(VERTICAL_START_POSITION); } - mOpenCloseAnimator.setValues( - PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); - mOpenCloseAnimator - .setDuration(mActivityContext.getDeviceProfile().bottomSheetOpenDuration) - .setInterpolator(AnimationUtils.loadInterpolator( - getContext(), android.R.interpolator.linear_out_slow_in)); - mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mOpenCloseAnimator.removeListener(this); - } - }); + setUpOpenAnimation(mActivityContext.getDeviceProfile().bottomSheetOpenDuration); + Animator animator = mOpenCloseAnimation.getAnimationPlayer(); + animator.setInterpolator(AnimationUtils.loadInterpolator( + getContext(), android.R.interpolator.linear_out_slow_in)); post(() -> { - mOpenCloseAnimator.start(); + animator.setDuration(mActivityContext.getDeviceProfile().bottomSheetOpenDuration) + .start(); mContent.animate().alpha(1).setDuration(FADE_IN_DURATION); }); } else { @@ -774,7 +765,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet super.onCloseComplete(); removeCallbacks(mShowEducationTipTask); if (mLatestEducationalTip != null) { - mLatestEducationalTip.close(false); + mLatestEducationalTip.close(true); } AccessibilityManagerCompat.sendStateEventToTest(getContext(), NORMAL_STATE_ORDINAL); } @@ -803,13 +794,15 @@ public class WidgetsFullSheet extends BaseWidgetSheet } // Checks the orientation of the screen - if (LARGE_SCREEN_WIDGET_PICKER.get() - && mOrientation != newConfig.orientation - && mDeviceProfile.isTablet - && !mDeviceProfile.isTwoPanels) { + if (mOrientation != newConfig.orientation) { mOrientation = newConfig.orientation; - handleClose(false); - show(Launcher.getLauncher(getContext()), false); + if (LARGE_SCREEN_WIDGET_PICKER.get() + && mDeviceProfile.isTablet && !mDeviceProfile.isTwoPanels) { + handleClose(false); + show(Launcher.getLauncher(getContext()), false); + } else { + reset(); + } } } @@ -891,6 +884,19 @@ public class WidgetsFullSheet extends BaseWidgetSheet return false; } + /** Gets the sheet for widget picker, which is used for testing. */ + @VisibleForTesting + public View getSheet() { + return mContent; + } + + /** Opens the first header in widget picker and scrolls to the top of the RecyclerView. */ + @VisibleForTesting + public void openFirstHeader() { + mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.selectFirstHeaderEntry(); + mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.scrollToTop(); + } + /** A holder class for holding adapters & their corresponding recycler view. */ final class AdapterHolder { static final int PRIMARY = 0; diff --git a/src/com/android/launcher3/workspace/WorkspaceSpecs.kt b/src/com/android/launcher3/workspace/WorkspaceSpecs.kt deleted file mode 100644 index ac0a166b18..0000000000 --- a/src/com/android/launcher3/workspace/WorkspaceSpecs.kt +++ /dev/null @@ -1,323 +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.launcher3.workspace - -import android.content.res.TypedArray -import android.content.res.XmlResourceParser -import android.util.AttributeSet -import android.util.Log -import android.util.TypedValue -import android.util.Xml -import com.android.launcher3.R -import com.android.launcher3.util.ResourceHelper -import java.io.IOException -import kotlin.math.roundToInt -import org.xmlpull.v1.XmlPullParser -import org.xmlpull.v1.XmlPullParserException - -private const val TAG = "WorkspaceSpecs" - -class WorkspaceSpecs(resourceHelper: ResourceHelper) { - object XmlTags { - const val WORKSPACE_SPECS = "workspaceSpecs" - - const val WORKSPACE_SPEC = "workspaceSpec" - const val START_PADDING = "startPadding" - const val END_PADDING = "endPadding" - const val GUTTER = "gutter" - const val CELL_SIZE = "cellSize" - } - - val workspaceHeightSpecList = mutableListOf<WorkspaceSpec>() - val workspaceWidthSpecList = mutableListOf<WorkspaceSpec>() - - init { - try { - val parser: XmlResourceParser = resourceHelper.getXml() - val depth = parser.depth - var type: Int - while ( - (parser.next().also { type = it } != XmlPullParser.END_TAG || - parser.depth > depth) && type != XmlPullParser.END_DOCUMENT - ) { - if (type == XmlPullParser.START_TAG && XmlTags.WORKSPACE_SPECS == parser.name) { - val displayDepth = parser.depth - while ( - (parser.next().also { type = it } != XmlPullParser.END_TAG || - parser.depth > displayDepth) && type != XmlPullParser.END_DOCUMENT - ) { - if ( - type == XmlPullParser.START_TAG && XmlTags.WORKSPACE_SPEC == parser.name - ) { - val attrs = - resourceHelper.obtainStyledAttributes( - Xml.asAttributeSet(parser), - R.styleable.WorkspaceSpec - ) - val maxAvailableSize = - attrs.getDimensionPixelSize( - R.styleable.WorkspaceSpec_maxAvailableSize, - 0 - ) - val specType = - WorkspaceSpec.SpecType.values()[ - attrs.getInt( - R.styleable.WorkspaceSpec_specType, - WorkspaceSpec.SpecType.HEIGHT.ordinal - )] - attrs.recycle() - - var startPadding: SizeSpec? = null - var endPadding: SizeSpec? = null - var gutter: SizeSpec? = null - var cellSize: SizeSpec? = null - - val limitDepth = parser.depth - while ( - (parser.next().also { type = it } != XmlPullParser.END_TAG || - parser.depth > limitDepth) && type != XmlPullParser.END_DOCUMENT - ) { - val attr: AttributeSet = Xml.asAttributeSet(parser) - if (type == XmlPullParser.START_TAG) { - when (parser.name) { - XmlTags.START_PADDING -> { - startPadding = SizeSpec(resourceHelper, attr) - } - XmlTags.END_PADDING -> { - endPadding = SizeSpec(resourceHelper, attr) - } - XmlTags.GUTTER -> { - gutter = SizeSpec(resourceHelper, attr) - } - XmlTags.CELL_SIZE -> { - cellSize = SizeSpec(resourceHelper, attr) - } - } - } - } - - if ( - startPadding == null || - endPadding == null || - gutter == null || - cellSize == null - ) { - throw IllegalStateException( - "All attributes in workspaceSpec must be defined" - ) - } - - val workspaceSpec = - WorkspaceSpec( - maxAvailableSize, - specType, - startPadding, - endPadding, - gutter, - cellSize - ) - if (workspaceSpec.isValid()) { - if (workspaceSpec.specType == WorkspaceSpec.SpecType.HEIGHT) - workspaceHeightSpecList.add(workspaceSpec) - else workspaceWidthSpecList.add(workspaceSpec) - } else { - throw IllegalStateException("Invalid workspaceSpec found.") - } - } - } - - if (workspaceWidthSpecList.isEmpty() || workspaceHeightSpecList.isEmpty()) { - throw IllegalStateException( - "WorkspaceSpecs is incomplete - " + - "height list size = ${workspaceHeightSpecList.size}; " + - "width list size = ${workspaceWidthSpecList.size}." - ) - } - } - } - parser.close() - } catch (e: Exception) { - when (e) { - is IOException, - is XmlPullParserException -> { - throw RuntimeException("Failure parsing workspaces specs file.", e) - } - else -> throw e - } - } - } - - /** - * Returns the CalculatedWorkspaceSpec for width, based on the available width and the - * WorkspaceSpecs. - */ - fun getCalculatedWidthSpec(columns: Int, availableWidth: Int): CalculatedWorkspaceSpec { - val widthSpec = workspaceWidthSpecList.first { availableWidth <= it.maxAvailableSize } - - return CalculatedWorkspaceSpec(availableWidth, columns, widthSpec) - } - - /** - * Returns the CalculatedWorkspaceSpec for height, based on the available height and the - * WorkspaceSpecs. - */ - fun getCalculatedHeightSpec(rows: Int, availableHeight: Int): CalculatedWorkspaceSpec { - val heightSpec = workspaceHeightSpecList.first { availableHeight <= it.maxAvailableSize } - - return CalculatedWorkspaceSpec(availableHeight, rows, heightSpec) - } -} - -class CalculatedWorkspaceSpec( - val availableSpace: Int, - val cells: Int, - val workspaceSpec: WorkspaceSpec -) { - var startPaddingPx: Int = 0 - private set - var endPaddingPx: Int = 0 - private set - var gutterPx: Int = 0 - private set - var cellSizePx: Int = 0 - private set - init { - // Calculate all fixed size first - if (workspaceSpec.startPadding.fixedSize > 0) - startPaddingPx = workspaceSpec.startPadding.fixedSize.roundToInt() - if (workspaceSpec.endPadding.fixedSize > 0) - endPaddingPx = workspaceSpec.endPadding.fixedSize.roundToInt() - if (workspaceSpec.gutter.fixedSize > 0) - gutterPx = workspaceSpec.gutter.fixedSize.roundToInt() - if (workspaceSpec.cellSize.fixedSize > 0) - cellSizePx = workspaceSpec.cellSize.fixedSize.roundToInt() - - // Calculate all available space next - if (workspaceSpec.startPadding.ofAvailableSpace > 0) - startPaddingPx = - (workspaceSpec.startPadding.ofAvailableSpace * availableSpace).roundToInt() - if (workspaceSpec.endPadding.ofAvailableSpace > 0) - endPaddingPx = (workspaceSpec.endPadding.ofAvailableSpace * availableSpace).roundToInt() - if (workspaceSpec.gutter.ofAvailableSpace > 0) - gutterPx = (workspaceSpec.gutter.ofAvailableSpace * availableSpace).roundToInt() - if (workspaceSpec.cellSize.ofAvailableSpace > 0) - cellSizePx = (workspaceSpec.cellSize.ofAvailableSpace * availableSpace).roundToInt() - - // Calculate remainder space last - val gutters = cells - 1 - val usedSpace = startPaddingPx + endPaddingPx + (gutterPx * gutters) + (cellSizePx * cells) - val remainderSpace = availableSpace - usedSpace - if (workspaceSpec.startPadding.ofRemainderSpace > 0) - startPaddingPx = - (workspaceSpec.startPadding.ofRemainderSpace * remainderSpace).roundToInt() - if (workspaceSpec.endPadding.ofRemainderSpace > 0) - endPaddingPx = (workspaceSpec.endPadding.ofRemainderSpace * remainderSpace).roundToInt() - if (workspaceSpec.gutter.ofRemainderSpace > 0) - gutterPx = (workspaceSpec.gutter.ofRemainderSpace * remainderSpace).roundToInt() - if (workspaceSpec.cellSize.ofRemainderSpace > 0) - cellSizePx = (workspaceSpec.cellSize.ofRemainderSpace * remainderSpace).roundToInt() - } -} - -data class WorkspaceSpec( - val maxAvailableSize: Int, - val specType: SpecType, - val startPadding: SizeSpec, - val endPadding: SizeSpec, - val gutter: SizeSpec, - val cellSize: SizeSpec -) { - - enum class SpecType { - HEIGHT, - WIDTH - } - - fun isValid(): Boolean { - if (maxAvailableSize <= 0) { - Log.e(TAG, "WorkspaceSpec#isValid - maxAvailableSize <= 0") - return false - } - - // All specs need to be individually valid - if (!allSpecsAreValid()) { - Log.e(TAG, "WorkspaceSpec#isValid - !allSpecsAreValid()") - return false - } - - return true - } - - private fun allSpecsAreValid(): Boolean = - startPadding.isValid() && endPadding.isValid() && gutter.isValid() && cellSize.isValid() -} - -class SizeSpec(resourceHelper: ResourceHelper, attrs: AttributeSet) { - val fixedSize: Float - val ofAvailableSpace: Float - val ofRemainderSpace: Float - - init { - val styledAttrs = resourceHelper.obtainStyledAttributes(attrs, R.styleable.SpecSize) - - fixedSize = getValue(styledAttrs, R.styleable.SpecSize_fixedSize) - ofAvailableSpace = getValue(styledAttrs, R.styleable.SpecSize_ofAvailableSpace) - ofRemainderSpace = getValue(styledAttrs, R.styleable.SpecSize_ofRemainderSpace) - - styledAttrs.recycle() - } - - private fun getValue(a: TypedArray, index: Int): Float { - if (a.getType(index) == TypedValue.TYPE_DIMENSION) { - return a.getDimensionPixelSize(index, 0).toFloat() - } else if (a.getType(index) == TypedValue.TYPE_FLOAT) { - return a.getFloat(index, 0f) - } - return 0f - } - - fun isValid(): Boolean { - // All attributes are empty - if (fixedSize < 0f && ofAvailableSpace <= 0f && ofRemainderSpace <= 0f) { - Log.e(TAG, "SizeSpec#isValid - all attributes are empty") - return false - } - - // More than one attribute is filled - val attrCount = - (if (fixedSize > 0) 1 else 0) + - (if (ofAvailableSpace > 0) 1 else 0) + - (if (ofRemainderSpace > 0) 1 else 0) - if (attrCount > 1) { - Log.e(TAG, "SizeSpec#isValid - more than one attribute is filled") - return false - } - - // Values should be between 0 and 1 - if (ofAvailableSpace !in 0f..1f || ofRemainderSpace !in 0f..1f) { - Log.e(TAG, "SizeSpec#isValid - values should be between 0 and 1") - return false - } - - return true - } - - override fun toString(): String { - return "SizeSpec(fixedSize=$fixedSize, ofAvailableSpace=$ofAvailableSpace, " + - "ofRemainderSpace=$ofRemainderSpace)" - } -} diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java index d6b41c9c60..599a59154a 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java @@ -16,6 +16,7 @@ package com.android.launcher3.uioverrides; +import android.app.ActivityOptions; import android.app.Person; import android.content.Context; import android.content.pm.LauncherActivityInfo; @@ -40,4 +41,11 @@ public class ApiWrapper { public static Map<String, LauncherActivityInfo> getActivityOverrides(Context context) { return Collections.emptyMap(); } + + /** + * Creates an ActivityOptions to play fade-out animation on closing targets + */ + public static ActivityOptions createFadeOutAnimOptions(Context context) { + return ActivityOptions.makeCustomAnimation(context, 0, android.R.anim.fade_out); + } } diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java index 772a995324..b62dbd1d95 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java @@ -15,7 +15,7 @@ */ package com.android.launcher3.uioverrides.states; -import static com.android.launcher3.anim.Interpolators.DEACCEL_2; +import static com.android.app.animation.Interpolators.DECELERATE; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS; import android.content.Context; @@ -80,7 +80,7 @@ public class AllAppsState extends LauncherState { @Override public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) { PageAlphaProvider superPageAlphaProvider = super.getWorkspacePageAlphaProvider(launcher); - return new PageAlphaProvider(DEACCEL_2) { + return new PageAlphaProvider(DECELERATE) { @Override public float getPageAlpha(int pageIndex) { return launcher.getDeviceProfile().isTablet diff --git a/tests/Android.bp b/tests/Android.bp index 5a59fa8bcd..fec7de98f5 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -50,14 +50,13 @@ filegroup { "src/com/android/launcher3/util/Wait.java", "src/com/android/launcher3/util/WidgetUtils.java", "src/com/android/launcher3/util/rule/FailureWatcher.java", - "src/com/android/launcher3/util/rule/LauncherActivityRule.java", "src/com/android/launcher3/util/rule/ViewCaptureRule.kt", "src/com/android/launcher3/util/rule/SamplerRule.java", "src/com/android/launcher3/util/rule/ScreenRecordRule.java", "src/com/android/launcher3/util/rule/ShellCommandRule.java", - "src/com/android/launcher3/util/rule/SimpleActivityRule.java", "src/com/android/launcher3/util/rule/TestStabilityRule.java", "src/com/android/launcher3/util/rule/TISBindRule.java", + "src/com/android/launcher3/util/viewcapture_analysis/*.java", "src/com/android/launcher3/testcomponent/BaseTestingActivity.java", "src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java", "src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java", @@ -87,6 +86,7 @@ android_library { "launcher_log_protos_lite", "truth", "platform-test-rules", + "testables", ], manifest: "AndroidManifest-common.xml", platform_apis: true, diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml index b17006181e..ee151bbeb4 100644 --- a/tests/AndroidManifest-common.xml +++ b/tests/AndroidManifest-common.xml @@ -32,6 +32,7 @@ <receiver android:name="com.android.launcher3.testcomponent.AppWidgetNoConfig" android:exported="true" + android:icon="@drawable/test_widget_no_config_icon" android:label="No Config"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> @@ -41,6 +42,17 @@ </receiver> <receiver + android:name="com.android.launcher3.testcomponent.AppWidgetNoConfigLarge" + android:exported="true" + android:label="No Config Large"> + <intent-filter> + <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> + </intent-filter> + <meta-data android:name="android.appwidget.provider" + android:resource="@xml/appwidget_no_config_large"/> + </receiver> + + <receiver android:name="com.android.launcher3.testcomponent.AppWdigetHidden" android:exported="true" android:label="Hidden widget"> @@ -54,6 +66,7 @@ <receiver android:name="com.android.launcher3.testcomponent.AppWidgetWithConfig" android:exported="true" + android:icon="@drawable/test_widget_with_config_icon" android:label="With Config"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> @@ -65,6 +78,7 @@ <receiver android:name="com.android.launcher3.testcomponent.AppWidgetWithDialog" android:exported="true" + android:icon="@drawable/test_widget_with_dialog_icon" android:label="With Dialog"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> @@ -76,6 +90,7 @@ <receiver android:name="com.android.launcher3.testcomponent.AppWidgetDynamicColors" android:exported="true" + android:icon="@drawable/test_widget_dynamic_colors_icon" android:label="Dynamic Colors"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> @@ -288,6 +303,28 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity-alias> + <activity-alias android:name="MaxShortcutsActivity" + android:label="TestActivityMaxShortcuts" + android:exported="true" + android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + <meta-data android:name="android.app.shortcuts" + android:resource="@xml/max_shortcuts"/> + </activity-alias> + <activity-alias android:name="SingleShortcutActivity" + android:label="TestActivitySingleShortcut" + android:exported="true" + android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + <meta-data android:name="android.app.shortcuts" + android:resource="@xml/single_shortcut"/> + </activity-alias> <activity android:name="com.android.launcher3.testcomponent.DialogTestActivity" android:label="Dialog Activity" @@ -308,6 +345,15 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <activity-alias android:name="WebSearchActivity" + android:label="WebSearchActivity" + android:exported="true" + android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity"> + <intent-filter> + <action android:name="android.intent.action.WEB_SEARCH" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity-alias> <!-- [b/197780098] Disable eager initialization of Jetpack libraries. --> <provider diff --git a/tests/assets/ReorderWidgets/multiple_cell_layouts_no_space_reorder b/tests/assets/ReorderWidgets/multiple_cell_layouts_no_space_reorder new file mode 100644 index 0000000000..c6d65f866a --- /dev/null +++ b/tests/assets/ReorderWidgets/multiple_cell_layouts_no_space_reorder @@ -0,0 +1,56 @@ +################################################################################################### +# This file contains test case composed of the following tags: +# * # (coments): Lines starting with this character would be ignored. +# * arguments: is set of words separated by spaces that can later be parsed +# * board: represent a workspace, the first line is the dimensions of the board width x height (wxh) +# There are different characters on the board that represent different things: +# * x: The x character represents spaces that would be ignored, for example it can be used in +# the first row if we don't know how wide the smartspace is. +# * i: Represents an icon on the workspace, none in particular just an icon +# * [a-z]: Represents a widget and it can be any number or character +# except any other already in use. The whole continuos are of the same character is the +# area of the widget. +# * [A-Z]: Represents a folder and number of icons in the folder is represented by the order of +# letter in the alphabet, A=2, B=3, C=4 ... etc. +# Test are parsed by CellLayoutTestCaseReader.java and boards are parsed by CellLayoutBoard.java +################################################################################################### +# 5x5 Test +board: 5x5 +xxxxx|aeeee +--mm-|acccc +--mm-|acccc +ggggg|acccc +ggggg|adddd +arguments: 7 1 +board: 10x5 +xxxxx|aeeee +---mm|acccc +---mm|acccc +ggggg|acccc +ggggg|adddd +# 4x4 Test +board: 4x4 +xxxx|aeee +--mm|accc +--mm|accc +gggg|accc +arguments: 5 1 +board: 8x4 +xxxx|aeee +--mm|accc +--mm|accc +gggg|accc +# 6x5 Test +board: 6x5 +xxxxxx|aeeeee +--mm--|accccc +--mm--|accccc +gggggg|accccc +gggggg|addddd +arguments: 8 1 +board: 12x5 +xxxxxx|aeeeee +----mm|accccc +----mm|accccc +gggggg|accccc +gggggg|addddd diff --git a/tests/assets/ReorderWidgets/multiple_cell_layouts_reorder_other_side b/tests/assets/ReorderWidgets/multiple_cell_layouts_reorder_other_side new file mode 100644 index 0000000000..376638ee19 --- /dev/null +++ b/tests/assets/ReorderWidgets/multiple_cell_layouts_reorder_other_side @@ -0,0 +1,56 @@ +################################################################################################### +# This file contains test case composed of the following tags: +# * # (coments): Lines starting with this character would be ignored. +# * arguments: is set of words separated by spaces that can later be parsed +# * board: represent a workspace, the first line is the dimensions of the board width x height (wxh) +# There are different characters on the board that represent different things: +# * x: The x character represents spaces that would be ignored, for example it can be used in +# the first row if we don't know how wide the smartspace is. +# * i: Represents an icon on the workspace, none in particular just an icon +# * [a-z]: Represents a widget and it can be any number or character +# except any other already in use. The whole continuos are of the same character is the +# area of the widget. +# * [A-Z]: Represents a folder and number of icons in the folder is represented by the order of +# letter in the alphabet, A=2, B=3, C=4 ... etc. +# Test are parsed by CellLayoutTestCaseReader.java and boards are parsed by CellLayoutBoard.java +################################################################################################### +# 5x5 Test +board: 5x5 +xxxxx|aaaaa +--mm-|plllh +--mm-|piiih +ggggg|piiih +ggggg|fffff +arguments: 7 1 +board: 10x5 +xxxxx|aaaaa +--lll|p-mmh +---ii|pimmh +ggggg|piiih +ggggg|fffff +# 4x4 Test +board: 4x4 +xxxx|aaaa +--mm|pllh +--mm|piih +gggg|ffff +arguments: 5 1 +board: 8x4 +xxxx|aaaa +--ll|pmmh +--ii|pmmh +gggg|ffff +# 6x5 Test +board: 6x5 +xxxxxx|aaaaaa +--mmm-|pllllh +--mmm-|piiiih +--mmm-|piiiih +gggggg|ffffff +arguments: 8 1 +board: 12x5 +xxxxxx|aaaaaa +--llll|p-mmmh +---iii|pimmmh +---iii|pimmmh +gggggg|ffffff
\ No newline at end of file diff --git a/tests/assets/ReorderWidgets/multiple_cell_layouts_simple_reorder b/tests/assets/ReorderWidgets/multiple_cell_layouts_simple_reorder new file mode 100644 index 0000000000..44301d29ef --- /dev/null +++ b/tests/assets/ReorderWidgets/multiple_cell_layouts_simple_reorder @@ -0,0 +1,56 @@ +################################################################################################### +# This file contains test case composed of the following tags: +# * # (coments): Lines starting with this character would be ignored. +# * arguments: is set of words separated by spaces that can later be parsed +# * board: represent a workspace, the first line is the dimensions of the board width x height (wxh) +# There are different characters on the board that represent different things: +# * x: The x character represents spaces that would be ignored, for example it can be used in +# the first row if we don't know how wide the smartspace is. +# * i: Represents an icon on the workspace, none in particular just an icon +# * [a-z]: Represents a widget and it can be any number or character +# except any other already in use. The whole continuos are of the same character is the +# area of the widget. +# * [A-Z]: Represents a folder and number of icons in the folder is represented by the order of +# letter in the alphabet, A=2, B=3, C=4 ... etc. +# Test are parsed by CellLayoutTestCaseReader.java and boards are parsed by CellLayoutBoard.java +################################################################################################### +# 5x5 Test +board: 5x5 +xxxxx|----- +--mm-|----- +--mm-|----- +-----|----- +-----|----- +arguments: 8 3 +board: 10x5 +xxxxx|----- +-----|----- +-----|----- +-----|---mm +-----|---mm +# 4x4 Test +board: 4x4 +xxxx|---- +--mm|---- +--mm|---- +----|---- +arguments: 5 3 +board: 8x4 +xxxx|---- +----|---- +----|-mm- +----|-mm- +# 6x5 Test +board: 6x5 +xxxxxx|------ +--m---|------ +------|------ +------|------ +------|------ +arguments: 10 4 +board: 12x5 +xxxxxx|------ +------|------ +------|------ +------|------ +------|----m-
\ No newline at end of file diff --git a/tests/res/drawable/test_widget_dynamic_colors_icon.xml b/tests/res/drawable/test_widget_dynamic_colors_icon.xml new file mode 100644 index 0000000000..69f66757f6 --- /dev/null +++ b/tests/res/drawable/test_widget_dynamic_colors_icon.xml @@ -0,0 +1,29 @@ +<?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. +--> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@android:color/white"/> + <foreground> + <color android:color="#964B00"/> + </foreground> + <monochrome> + <vector android:width="48dp" android:height="48dp" android:viewportWidth="48.0" android:viewportHeight="48.0"> + <path + android:fillColor="#FF000000" + android:pathData="M0,24L48,24 48,48, 0,48 Z"/> + </vector> + </monochrome> +</adaptive-icon> diff --git a/tests/res/drawable/test_widget_no_config_icon.xml b/tests/res/drawable/test_widget_no_config_icon.xml new file mode 100644 index 0000000000..e3d012529d --- /dev/null +++ b/tests/res/drawable/test_widget_no_config_icon.xml @@ -0,0 +1,29 @@ +<?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. +--> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@android:color/white"/> + <foreground> + <color android:color="#00FFFF"/> + </foreground> + <monochrome> + <vector android:width="48dp" android:height="48dp" android:viewportWidth="48.0" android:viewportHeight="48.0"> + <path + android:fillColor="#FF000000" + android:pathData="M0,24L48,24 48,48, 0,48 Z"/> + </vector> + </monochrome> +</adaptive-icon> diff --git a/tests/res/drawable/test_widget_with_config_icon.xml b/tests/res/drawable/test_widget_with_config_icon.xml new file mode 100644 index 0000000000..98b797b870 --- /dev/null +++ b/tests/res/drawable/test_widget_with_config_icon.xml @@ -0,0 +1,29 @@ +<?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. +--> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@android:color/white"/> + <foreground> + <color android:color="#008000" /> + </foreground> + <monochrome> + <vector android:width="48dp" android:height="48dp" android:viewportWidth="48.0" android:viewportHeight="48.0"> + <path + android:fillColor="#FF000000" + android:pathData="M0,24L48,24 48,48, 0,48 Z"/> + </vector> + </monochrome> +</adaptive-icon> diff --git a/tests/res/drawable/test_widget_with_dialog_icon.xml b/tests/res/drawable/test_widget_with_dialog_icon.xml new file mode 100644 index 0000000000..d2879d281b --- /dev/null +++ b/tests/res/drawable/test_widget_with_dialog_icon.xml @@ -0,0 +1,29 @@ +<?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. +--> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@android:color/white"/> + <foreground> + <color android:color="#800080"/> + </foreground> + <monochrome> + <vector android:width="48dp" android:height="48dp" android:viewportWidth="48.0" android:viewportHeight="48.0"> + <path + android:fillColor="#FF000000" + android:pathData="M0,24L48,24 48,48, 0,48 Z"/> + </vector> + </monochrome> +</adaptive-icon> diff --git a/tests/res/raw/cache_data_updated_task_data.txt b/tests/res/raw/cache_data_updated_task_data.txt deleted file mode 100644 index 603dbe3f92..0000000000 --- a/tests/res/raw/cache_data_updated_task_data.txt +++ /dev/null @@ -1,28 +0,0 @@ -# Model data used by CacheDataUpdatedTaskTest - -classMap s com.android.launcher3.model.data.WorkspaceItemInfo - -# Items for the BgDataModel - -# App shortcuts -bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1 -bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2 -bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3 -bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4 - -# Auto install app shortcut -bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5 -bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6 - -# Custom shortcuts -bgItem s itemType=1 title=app1-shrt intent=component=app1/class3 id=7 -bgItem s itemType=1 title=app4-shrt intent=component=app4/class1 id=8 - -# Restored custom shortcut -bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=9 -bgItem s itemType=1 status=1 title=app5-shrt intent=component=app5/class1 id=10 - -allApps componentName=app1/class1 intent=component=app1/class1 -allApps componentName=app1/class2 intent=component=app1/class2 -allApps componentName=app2/class1 intent=component=app2/class1 -allApps componentName=app2/class2 intent=component=app2/class2
\ No newline at end of file diff --git a/tests/res/raw/package_install_state_change_task_data.txt b/tests/res/raw/package_install_state_change_task_data.txt deleted file mode 100644 index e82ea9d4d6..0000000000 --- a/tests/res/raw/package_install_state_change_task_data.txt +++ /dev/null @@ -1,24 +0,0 @@ -# Model data used by PackageInstallStateChangeTaskTest - -classMap s com.android.launcher3.model.data.WorkspaceItemInfo -classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo - -# Items for the BgDataModel - -# App shortcuts -bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1 -bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2 -bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3 -bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4 - -# Promise icons for app3 -bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5 -bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6 -bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=7 - -# Promise icon for app4 -bgItem s itemType=1 status=1 title=app4-shrt intent=component=app4/class1 id=8 - -# Widget -bgItem w providerName=app4/provider1 id=9 -bgItem w providerName=app5/provider1 id=10
\ No newline at end of file diff --git a/tests/res/raw/widgets_predication_update_task_data.txt b/tests/res/raw/widgets_predication_update_task_data.txt deleted file mode 100644 index 941d1954e2..0000000000 --- a/tests/res/raw/widgets_predication_update_task_data.txt +++ /dev/null @@ -1,24 +0,0 @@ -# Model data used by WidgetsPredictionUpdateTasksTest - -classMap s com.android.launcher3.model.data.WorkspaceItemInfo -classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo - -# Items for the BgDataModel - -# App shortcuts -bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1 -bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2 -bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3 -bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4 - -# Promise icons for app3 -bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5 -bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6 -bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=7 - -# Promise icon for app4 -bgItem s itemType=1 status=1 title=app4-shrt intent=component=app4/class1 id=8 - -# Widget -bgItem w providerName=app4/provider1 id=9 -bgItem w providerName=app5/provider1 id=10
\ No newline at end of file diff --git a/tests/res/values/attrs.xml b/tests/res/values/attrs.xml index 2310d9ef66..e5ee06473c 100644 --- a/tests/res/values/attrs.xml +++ b/tests/res/values/attrs.xml @@ -18,7 +18,7 @@ <!-- Attributes have to be copied to test for correct parsing of files --> <resources> <!-- Responsive grids attributes --> - <declare-styleable name="WorkspaceSpec"> + <declare-styleable name="ResponsiveSpec"> <attr name="specType" format="integer"> <enum name="height" value="0" /> <enum name="width" value="1" /> @@ -26,10 +26,26 @@ <attr name="maxAvailableSize" format="dimension" /> </declare-styleable> - <declare-styleable name="SpecSize"> + <declare-styleable name="WorkspaceSpec"> + <attr name="specType" /> + <attr name="maxAvailableSize" /> + </declare-styleable> + + <declare-styleable name="FolderSpec"> + <attr name="specType" /> + <attr name="maxAvailableSize" /> + </declare-styleable> + + <declare-styleable name="AllAppsSpec"> + <attr name="specType" /> + <attr name="maxAvailableSize" /> + </declare-styleable> + + <declare-styleable name="SizeSpec"> <attr name="fixedSize" format="dimension" /> <attr name="ofAvailableSpace" format="float" /> <attr name="ofRemainderSpace" format="float" /> + <attr name="matchWorkspace" format="boolean" /> + <attr name="maxSize" format="dimension" /> </declare-styleable> - </resources> diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml index 0ad87fb9cd..54ade56f19 100644 --- a/tests/res/values/strings.xml +++ b/tests/res/values/strings.xml @@ -3,4 +3,5 @@ <string name="shortcut1" translatable="false">Shortcut 1</string> <string name="shortcut2" translatable="false">Shortcut 2</string> <string name="shortcut3" translatable="false">Shortcut 3</string> + <string name="shortcut4" translatable="false">Shortcut 4</string> </resources> diff --git a/tests/res/xml/appwidget_no_config_large.xml b/tests/res/xml/appwidget_no_config_large.xml new file mode 100644 index 0000000000..b3b69d92d7 --- /dev/null +++ b/tests/res/xml/appwidget_no_config_large.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<appwidget-provider + xmlns:android="http://schemas.android.com/apk/res/android" + android:minWidth="1dp" + android:minHeight="1dp" + android:minResizeWidth="1dp" + android:maxResizeWidth="3000dp" + android:targetCellHeight="1" + android:targetCellWidth="5" + android:updatePeriodMillis="86400000" + android:initialLayout="@layout/test_layout_appwidget_red" + android:previewLayout="@layout/test_layout_appwidget_red" + android:resizeMode="horizontal|vertical" + android:widgetCategory="home_screen"> +</appwidget-provider>
\ No newline at end of file diff --git a/tests/res/xml/invalid_all_apps_file_case_1.xml b/tests/res/xml/invalid_all_apps_file_case_1.xml new file mode 100644 index 0000000000..6fd35b161c --- /dev/null +++ b/tests/res/xml/invalid_all_apps_file_case_1.xml @@ -0,0 +1,36 @@ +<?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. + --> +<allAppsSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto"> + <allAppsSpec + launcher:specType="height" + launcher:maxAvailableSize="9999dp"> + <!-- missing startPadding --> + <endPadding launcher:fixedSize="0dp" /> + <gutter launcher:matchWorkspace="true" /> + <cellSize launcher:matchWorkspace="true" /> + </allAppsSpec> + + <allAppsSpec + launcher:specType="width" + launcher:maxAvailableSize="9999dp"> + <startPadding launcher:matchWorkspace="true" /> + <endPadding launcher:matchWorkspace="true" /> + <gutter launcher:matchWorkspace="true" /> + <cellSize launcher:matchWorkspace="true" /> + </allAppsSpec> + +</allAppsSpecs> + diff --git a/tests/res/xml/invalid_all_apps_file_case_2.xml b/tests/res/xml/invalid_all_apps_file_case_2.xml new file mode 100644 index 0000000000..de9c1ac89b --- /dev/null +++ b/tests/res/xml/invalid_all_apps_file_case_2.xml @@ -0,0 +1,38 @@ +<?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. + --> +<allAppsSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto"> + <allAppsSpec + launcher:specType="height" + launcher:maxAvailableSize="9999dp"> + <startPadding launcher:fixedSize="0dp" /> + <endPadding launcher:fixedSize="0dp" /> + <!-- more than 1 value in one tag --> + <gutter + launcher:matchWorkspace="true" + launcher:fixedSize="16dp" /> + <cellSize launcher:matchWorkspace="true" /> + </allAppsSpec> + + <allAppsSpec + launcher:specType="width" + launcher:maxAvailableSize="9999dp"> + <startPadding launcher:matchWorkspace="true" /> + <endPadding launcher:matchWorkspace="true" /> + <gutter launcher:matchWorkspace="true" /> + <cellSize launcher:matchWorkspace="true" /> + </allAppsSpec> + +</allAppsSpecs>
\ No newline at end of file diff --git a/tests/res/xml/invalid_all_apps_file_case_3.xml b/tests/res/xml/invalid_all_apps_file_case_3.xml new file mode 100644 index 0000000000..7af0af4971 --- /dev/null +++ b/tests/res/xml/invalid_all_apps_file_case_3.xml @@ -0,0 +1,37 @@ +<?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. + --> +<allAppsSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto"> + <allAppsSpec + launcher:specType="height" + launcher:maxAvailableSize="9999dp"> + <startPadding launcher:fixedSize="0dp" /> + <endPadding launcher:fixedSize="0dp" /> + <gutter launcher:matchWorkspace="true" /> + <!-- value bigger than 1 --> + <cellSize launcher:ofRemainderSpace="1.001" /> + </allAppsSpec> + + <allAppsSpec + launcher:specType="width" + launcher:maxAvailableSize="9999dp"> + <startPadding launcher:matchWorkspace="true" /> + <endPadding launcher:matchWorkspace="true" /> + <gutter launcher:matchWorkspace="true" /> + <cellSize launcher:matchWorkspace="true" /> + </allAppsSpec> + +</allAppsSpecs> + diff --git a/tests/res/xml/invalid_folders_specs_1.xml b/tests/res/xml/invalid_folders_specs_1.xml new file mode 100644 index 0000000000..0864249f02 --- /dev/null +++ b/tests/res/xml/invalid_folders_specs_1.xml @@ -0,0 +1,40 @@ +<?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. + --> + +<!-- Tablet - 6x5 portrait --> +<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto"> + <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp"> + <!-- missing startPadding --> + <endPadding launcher:fixedSize="16dp" /> + <gutter launcher:fixedSize="16dp" /> + <cellSize launcher:matchWorkspace="true" /> + </folderSpec> + <folderSpec launcher:specType="width" launcher:maxAvailableSize="9999dp"> + <startPadding launcher:fixedSize="16dp" /> + <endPadding launcher:fixedSize="16dp" /> + <gutter launcher:fixedSize="16dp" /> + <cellSize launcher:fixedSize="102dp" /> + </folderSpec> + + <!-- Height spec is fixed --> + <folderSpec launcher:specType="height" launcher:maxAvailableSize="9999dp"> + <startPadding launcher:fixedSize="24dp" /> + <!-- mapped to footer height size --> + <endPadding launcher:fixedSize="64dp" /> + <gutter launcher:fixedSize="16dp" /> + <cellSize launcher:fixedSize="104dp" /> + </folderSpec> +</folderSpecs> diff --git a/tests/res/xml/invalid_folders_specs_2.xml b/tests/res/xml/invalid_folders_specs_2.xml new file mode 100644 index 0000000000..0b7dd627d6 --- /dev/null +++ b/tests/res/xml/invalid_folders_specs_2.xml @@ -0,0 +1,43 @@ +<?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. + --> + +<!-- Tablet - 6x5 portrait --> +<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto"> + <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp"> + <startPadding launcher:fixedSize="16dp" /> + <endPadding launcher:fixedSize="16dp" /> + <!-- more than 1 value in one tag --> + <gutter + launcher:ofAvailableSpace="0.0125" + launcher:fixedSize="16dp" /> + <cellSize launcher:matchWorkspace="true" /> + </folderSpec> + <folderSpec launcher:specType="width" launcher:maxAvailableSize="9999dp"> + <startPadding launcher:fixedSize="16dp" /> + <endPadding launcher:fixedSize="16dp" /> + <gutter launcher:fixedSize="16dp" /> + <cellSize launcher:fixedSize="102dp" /> + </folderSpec> + + <!-- Height spec is fixed --> + <folderSpec launcher:specType="height" launcher:maxAvailableSize="9999dp"> + <startPadding launcher:fixedSize="24dp" /> + <!-- mapped to footer height size --> + <endPadding launcher:fixedSize="64dp" /> + <gutter launcher:fixedSize="16dp" /> + <cellSize launcher:fixedSize="104dp" /> + </folderSpec> +</folderSpecs> diff --git a/tests/res/xml/invalid_folders_specs_3.xml b/tests/res/xml/invalid_folders_specs_3.xml new file mode 100644 index 0000000000..83fd3e167f --- /dev/null +++ b/tests/res/xml/invalid_folders_specs_3.xml @@ -0,0 +1,41 @@ +<?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. + --> + +<!-- Tablet - 6x5 portrait - More the one value first gutter --> +<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto"> + <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp"> + <startPadding launcher:fixedSize="16dp" /> + <endPadding launcher:fixedSize="16dp" /> + <gutter launcher:fixedSize="16dp" /> + <!-- value bigger than 1 --> + <cellSize launcher:ofRemainderSpace="1.001" /> + </folderSpec> + <folderSpec launcher:specType="width" launcher:maxAvailableSize="9999dp"> + <startPadding launcher:fixedSize="16dp" /> + <endPadding launcher:fixedSize="16dp" /> + <gutter launcher:fixedSize="16dp" /> + <cellSize launcher:fixedSize="102dp" /> + </folderSpec> + + <!-- Height spec is fixed --> + <folderSpec launcher:specType="height" launcher:maxAvailableSize="9999dp"> + <startPadding launcher:fixedSize="24dp" /> + <!-- mapped to footer height size --> + <endPadding launcher:fixedSize="64dp" /> + <gutter launcher:fixedSize="16dp" /> + <cellSize launcher:fixedSize="104dp" /> + </folderSpec> +</folderSpecs> diff --git a/res/interpolator/fast_out_extra_slow_in.xml b/tests/res/xml/invalid_folders_specs_4.xml index f296a8224f..2d8c730164 100644 --- a/res/interpolator/fast_out_extra_slow_in.xml +++ b/tests/res/xml/invalid_folders_specs_4.xml @@ -1,6 +1,5 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2022 The Android Open Source Project +<?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. @@ -12,8 +11,14 @@ ~ 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 + ~ limitations under the License. --> - -<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" - android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
\ No newline at end of file +<!-- missing height spec --> +<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto"> + <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp"> + <startPadding launcher:fixedSize="16dp" /> + <endPadding launcher:fixedSize="16dp" /> + <gutter launcher:fixedSize="16dp" /> + <cellSize launcher:matchWorkspace="true" /> + </folderSpec> +</folderSpecs> diff --git a/tests/res/xml/invalid_folders_specs_5.xml b/tests/res/xml/invalid_folders_specs_5.xml new file mode 100644 index 0000000000..b4f1f4dce9 --- /dev/null +++ b/tests/res/xml/invalid_folders_specs_5.xml @@ -0,0 +1,33 @@ +<?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. + --> +<!-- missing breakpoints > 800dp --> +<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto"> + <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp"> + <startPadding launcher:fixedSize="16dp" /> + <endPadding launcher:fixedSize="16dp" /> + <gutter launcher:fixedSize="16dp" /> + <cellSize launcher:matchWorkspace="true" /> + </folderSpec> + + <!-- Height spec is fixed --> + <folderSpec launcher:specType="height" launcher:maxAvailableSize="800dp"> + <startPadding launcher:fixedSize="24dp" /> + <!-- mapped to footer height size --> + <endPadding launcher:fixedSize="64dp" /> + <gutter launcher:fixedSize="16dp" /> + <cellSize launcher:fixedSize="104dp" /> + </folderSpec> +</folderSpecs> diff --git a/tests/res/xml/invalid_hotseat_file_case_1.xml b/tests/res/xml/invalid_hotseat_file_case_1.xml new file mode 100644 index 0000000000..fcbc5ead8d --- /dev/null +++ b/tests/res/xml/invalid_hotseat_file_case_1.xml @@ -0,0 +1,30 @@ +<?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. + --> +<hotseatSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto"> + + <hotseatSpec + launcher:specType="height" + launcher:maxAvailableSize="847dp"> + <hotseatQsbSpace launcher:ofAvailableSpace="1" /> + </hotseatSpec> + + <hotseatSpec + launcher:specType="height" + launcher:maxAvailableSize="9999dp"> + <hotseatQsbSpace launcher:fixedSize="36dp" /> + </hotseatSpec> + +</hotseatSpecs>
\ No newline at end of file diff --git a/tests/res/xml/invalid_workspace_file_case_4.xml b/tests/res/xml/invalid_workspace_file_case_4.xml new file mode 100644 index 0000000000..9e74c852b3 --- /dev/null +++ b/tests/res/xml/invalid_workspace_file_case_4.xml @@ -0,0 +1,58 @@ +<?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. + --> + +<workspaceSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto"> + <workspaceSpec + launcher:specType="height" + launcher:maxAvailableSize="648dp"> + <startPadding + launcher:ofAvailableSpace="0.0125" /> + <endPadding + launcher:ofAvailableSpace="0.05" /> + <!-- value in workspace spec using matchWorkspace --> + <gutter + launcher:matchWorkspace="true" /> + <cellSize + launcher:ofRemainderSpace="0.2" /> + </workspaceSpec> + + <workspaceSpec + launcher:specType="height" + launcher:maxAvailableSize="9999dp"> + <startPadding + launcher:ofAvailableSpace="0.0306" /> + <endPadding + launcher:ofAvailableSpace="0.068" /> + <gutter + launcher:fixedSize="16dp" /> + <cellSize + launcher:ofRemainderSpace="0.2" /> + </workspaceSpec> + + <!-- Width spec is always the same --> + <workspaceSpec + launcher:specType="width" + launcher:maxAvailableSize="9999dp"> + <startPadding + launcher:ofRemainderSpace="0.21436227" /> + <endPadding + launcher:ofRemainderSpace="0.21436227" /> + <gutter + launcher:ofRemainderSpace="0.11425509" /> + <cellSize + launcher:fixedSize="120dp" /> + </workspaceSpec> +</workspaceSpecs> diff --git a/tests/res/xml/max_shortcuts.xml b/tests/res/xml/max_shortcuts.xml new file mode 100644 index 0000000000..312a24c918 --- /dev/null +++ b/tests/res/xml/max_shortcuts.xml @@ -0,0 +1,39 @@ +<?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. + --> +<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" > + <shortcut + android:shortcutId="max_shortcut1" + android:icon="@drawable/test_theme_icon" + android:shortcutShortLabel="@string/shortcut1"> + <intent android:action="com.android.launcher3.intent.action.test_shortcut_max"/> + </shortcut> + <shortcut + android:shortcutId="max_shortcut2" + android:shortcutShortLabel="@string/shortcut2"> + <intent android:action="com.android.launcher3.intent.action.test_shortcut_max"/> + </shortcut> + <shortcut + android:shortcutId="max_shortcut3" + android:shortcutShortLabel="@string/shortcut3"> + <intent android:action="com.android.launcher3.intent.action.test_shortcut_max"/> + </shortcut> + <shortcut + android:shortcutId="max_shortcut4" + android:shortcutShortLabel="@string/shortcut4"> + <intent android:action="com.android.launcher3.intent.action.test_shortcut_max"/> + </shortcut> +</shortcuts> diff --git a/tests/res/xml/shortcuts.xml b/tests/res/xml/shortcuts.xml index 94e8edd936..2ba9d7fb11 100644 --- a/tests/res/xml/shortcuts.xml +++ b/tests/res/xml/shortcuts.xml @@ -1,4 +1,19 @@ <?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. + --> <shortcuts xmlns:android="http://schemas.android.com/apk/res/android" > <shortcut android:shortcutId="shortcut1_themed" diff --git a/res/interpolator/standard_decelerate.xml b/tests/res/xml/single_shortcut.xml index 579f4f5644..e8d938fa11 100644 --- a/res/interpolator/standard_decelerate.xml +++ b/tests/res/xml/single_shortcut.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2022 The Android Open Source Project + ~ Copyright (C) 2023 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -14,9 +14,11 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - -<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" - android:controlX1="0" - android:controlY1="0" - android:controlX2="0" - android:controlY2="1"/>
\ No newline at end of file +<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" > + <shortcut + android:shortcutId="single_shortcut_themed" + android:icon="@drawable/test_theme_icon" + android:shortcutShortLabel="@string/shortcut1"> + <intent android:action="com.android.launcher3.intent.action.test_shortcut_single"/> + </shortcut> +</shortcuts> diff --git a/tests/res/xml/valid_all_apps_file.xml b/tests/res/xml/valid_all_apps_file.xml new file mode 100644 index 0000000000..0be55d13aa --- /dev/null +++ b/tests/res/xml/valid_all_apps_file.xml @@ -0,0 +1,36 @@ +<?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. + --> + +<allAppsSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto"> + <allAppsSpec + launcher:specType="height" + launcher:maxAvailableSize="9999dp"> + <startPadding launcher:fixedSize="0dp" /> + <endPadding launcher:fixedSize="0dp" /> + <gutter launcher:matchWorkspace="true" /> + <cellSize launcher:matchWorkspace="true" /> + </allAppsSpec> + + <allAppsSpec + launcher:specType="width" + launcher:maxAvailableSize="9999dp"> + <startPadding launcher:matchWorkspace="true" /> + <endPadding launcher:matchWorkspace="true" /> + <gutter launcher:matchWorkspace="true" /> + <cellSize launcher:matchWorkspace="true" /> + </allAppsSpec> + +</allAppsSpecs> diff --git a/tests/res/xml/valid_folders_specs.xml b/tests/res/xml/valid_folders_specs.xml new file mode 100644 index 0000000000..0c45544752 --- /dev/null +++ b/tests/res/xml/valid_folders_specs.xml @@ -0,0 +1,38 @@ +<?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. + --> +<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto"> + <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp"> + <startPadding launcher:fixedSize="16dp" /> + <endPadding launcher:fixedSize="16dp" /> + <gutter launcher:fixedSize="16dp" /> + <cellSize launcher:matchWorkspace="true" /> + </folderSpec> + <folderSpec launcher:specType="width" launcher:maxAvailableSize="9999dp"> + <startPadding launcher:fixedSize="16dp" /> + <endPadding launcher:fixedSize="16dp" /> + <gutter launcher:fixedSize="16dp" /> + <cellSize launcher:fixedSize="102dp" /> + </folderSpec> + + <!-- Height spec is fixed --> + <folderSpec launcher:specType="height" launcher:maxAvailableSize="9999dp"> + <startPadding launcher:fixedSize="24dp" /> + <!-- mapped to footer height size --> + <endPadding launcher:fixedSize="64dp" /> + <gutter launcher:fixedSize="16dp" /> + <cellSize launcher:matchWorkspace="true" /> + </folderSpec> +</folderSpecs> diff --git a/tests/res/xml/valid_hotseat_file.xml b/tests/res/xml/valid_hotseat_file.xml new file mode 100644 index 0000000000..c7f52e82c2 --- /dev/null +++ b/tests/res/xml/valid_hotseat_file.xml @@ -0,0 +1,30 @@ +<?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. + --> +<hotseatSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto"> + + <hotseatSpec + launcher:specType="height" + launcher:maxAvailableSize="847dp"> + <hotseatQsbSpace launcher:fixedSize="24dp" /> + </hotseatSpec> + + <hotseatSpec + launcher:specType="height" + launcher:maxAvailableSize="9999dp"> + <hotseatQsbSpace launcher:fixedSize="36dp" /> + </hotseatSpec> + +</hotseatSpecs>
\ No newline at end of file diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java index 2ac0dd7419..825b3dc671 100644 --- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java +++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java @@ -39,7 +39,6 @@ public final class TestProtocol { public static final int HINT_STATE_TWO_BUTTON_ORDINAL = 8; public static final int OVERVIEW_SPLIT_SELECT_ORDINAL = 9; public static final int EDIT_MODE_STATE_ORDINAL = 10; - public static final String TAPL_EVENTS_TAG = "TaplEvents"; public static final String SEQUENCE_MAIN = "Main"; public static final String SEQUENCE_TIS = "TIS"; public static final String SEQUENCE_PILFER = "Pilfer"; @@ -110,6 +109,7 @@ public final class TestProtocol { public static final String REQUEST_GET_TEST_EVENTS = "get-test-events"; public static final String REQUEST_GET_HAD_NONTEST_EVENTS = "get-had-nontest-events"; public static final String REQUEST_STOP_EVENT_LOGGING = "stop-event-logging"; + public static final String REQUEST_REINITIALIZE_DATA = "reinitialize-data"; public static final String REQUEST_CLEAR_DATA = "clear-data"; public static final String REQUEST_HOTSEAT_ICON_NAMES = "get-hotseat-icon-names"; public static final String REQUEST_IS_TABLET = "is-tablet"; @@ -152,15 +152,20 @@ public final class TestProtocol { public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation"; public static final String PERMANENT_DIAG_TAG = "TaplTarget"; - public static final String WORK_TAB_MISSING = "b/243688989"; public static final String TWO_TASKBAR_LONG_CLICKS = "b/262282528"; - public static final String FLAKY_ACTIVITY_COUNT = "b/260260325"; + public static final String FLAKY_QUICK_SWITCH_TO_PREVIOUS_APP = "b/286084688"; + public static final String ICON_MISSING = "b/282963545"; + public static final String LAUNCH_SPLIT_PAIR = "b/288939273"; + public static final String OVERVIEW_OVER_HOME = "b/279059025"; + public static final String INCORRECT_HOME_STATE = "b/293191790"; public static final String REQUEST_EMULATE_DISPLAY = "emulate-display"; public static final String REQUEST_STOP_EMULATE_DISPLAY = "stop-emulate-display"; public static final String REQUEST_IS_EMULATE_DISPLAY_RUNNING = "is-emulate-display-running"; public static final String REQUEST_EMULATE_PRINT_DEVICE = "emulate-print-device"; + public static final String REQUEST_FLAG_ENABLE_GRID_ONLY_OVERVIEW = "enable-grid-only-overview"; + /** Logs {@link Log#d(String, String)} if {@link #sDebugTracing} is true. */ public static void testLogD(String tag, String message) { if (!sDebugTracing) { diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt index 3de4d555ee..dd79ca8b02 100644 --- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt +++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt @@ -22,11 +22,16 @@ import android.graphics.Rect import android.util.DisplayMetrics import android.view.Surface import androidx.test.core.app.ApplicationProvider +import com.android.launcher3.testing.shared.ResourceUtils import com.android.launcher3.util.DisplayController import com.android.launcher3.util.NavigationMode import com.android.launcher3.util.WindowBounds import com.android.launcher3.util.window.CachedDisplayInfo import com.android.launcher3.util.window.WindowManagerProxy +import java.io.BufferedReader +import java.io.File +import java.io.PrintWriter +import java.io.StringWriter import kotlin.math.max import kotlin.math.min import org.junit.After @@ -154,41 +159,55 @@ abstract class AbstractDeviceProfileTest { } protected fun initializeVarsForTwoPanel( - deviceTabletSpec: DeviceSpec, - deviceSpec: DeviceSpec, + deviceSpecUnfolded: DeviceSpec, + deviceSpecFolded: DeviceSpec, isLandscape: Boolean = false, - isGestureMode: Boolean = true + isGestureMode: Boolean = true, + isFolded: Boolean = false ) { - val (tabletNaturalX, tabletNaturalY) = deviceTabletSpec.naturalSize - val tabletWindowsBounds = - tabletWindowsBounds(deviceTabletSpec, tabletNaturalX, tabletNaturalY) - val tabletDisplayInfo = + val (unfoldedNaturalX, unfoldedNaturalY) = deviceSpecUnfolded.naturalSize + val unfoldedWindowsBounds = + tabletWindowsBounds(deviceSpecUnfolded, unfoldedNaturalX, unfoldedNaturalY) + val unfoldedDisplayInfo = CachedDisplayInfo( - Point(tabletNaturalX, tabletNaturalY), + Point(unfoldedNaturalX, unfoldedNaturalY), Surface.ROTATION_0, Rect(0, 0, 0, 0) ) - val (phoneNaturalX, phoneNaturalY) = deviceSpec.naturalSize - val phoneWindowsBounds = - phoneWindowsBounds(deviceSpec, isGestureMode, phoneNaturalX, phoneNaturalY) - val phoneDisplayInfo = + val (foldedNaturalX, foldedNaturalY) = deviceSpecFolded.naturalSize + val foldedWindowsBounds = + phoneWindowsBounds(deviceSpecFolded, isGestureMode, foldedNaturalX, foldedNaturalY) + val foldedDisplayInfo = CachedDisplayInfo( - Point(phoneNaturalX, phoneNaturalY), + Point(foldedNaturalX, foldedNaturalY), Surface.ROTATION_0, Rect(0, 0, 0, 0) ) val perDisplayBoundsCache = - mapOf(tabletDisplayInfo to tabletWindowsBounds, phoneDisplayInfo to phoneWindowsBounds) + mapOf( + unfoldedDisplayInfo to unfoldedWindowsBounds, + foldedDisplayInfo to foldedWindowsBounds + ) - initializeCommonVars( - perDisplayBoundsCache, - tabletDisplayInfo, - rotation = if (isLandscape) Surface.ROTATION_0 else Surface.ROTATION_90, - isGestureMode, - densityDpi = deviceTabletSpec.densityDpi - ) + if (isFolded) { + initializeCommonVars( + perDisplayBoundsCache = perDisplayBoundsCache, + displayInfo = foldedDisplayInfo, + rotation = if (isLandscape) Surface.ROTATION_90 else Surface.ROTATION_0, + isGestureMode = isGestureMode, + densityDpi = deviceSpecFolded.densityDpi + ) + } else { + initializeCommonVars( + perDisplayBoundsCache = perDisplayBoundsCache, + displayInfo = unfoldedDisplayInfo, + rotation = if (isLandscape) Surface.ROTATION_0 else Surface.ROTATION_90, + isGestureMode = isGestureMode, + densityDpi = deviceSpecUnfolded.densityDpi + ) + } } private fun phoneWindowsBounds( @@ -196,7 +215,7 @@ abstract class AbstractDeviceProfileTest { isGestureMode: Boolean, naturalX: Int, naturalY: Int - ): Array<WindowBounds> { + ): List<WindowBounds> { val buttonsNavHeight = Utilities.dpToPx(48f, deviceSpec.densityDpi) val rotation0Insets = @@ -231,7 +250,7 @@ abstract class AbstractDeviceProfileTest { if (isGestureMode) deviceSpec.gesturePx else 0 ) - return arrayOf( + return listOf( WindowBounds(Rect(0, 0, naturalX, naturalY), rotation0Insets, Surface.ROTATION_0), WindowBounds(Rect(0, 0, naturalY, naturalX), rotation90Insets, Surface.ROTATION_90), WindowBounds(Rect(0, 0, naturalX, naturalY), rotation180Insets, Surface.ROTATION_180), @@ -243,11 +262,11 @@ abstract class AbstractDeviceProfileTest { deviceSpec: DeviceSpec, naturalX: Int, naturalY: Int - ): Array<WindowBounds> { + ): List<WindowBounds> { val naturalInsets = Rect(0, deviceSpec.statusBarNaturalPx, 0, 0) val rotatedInsets = Rect(0, deviceSpec.statusBarRotatedPx, 0, 0) - return arrayOf( + return listOf( WindowBounds(Rect(0, 0, naturalX, naturalY), naturalInsets, Surface.ROTATION_0), WindowBounds(Rect(0, 0, naturalY, naturalX), rotatedInsets, Surface.ROTATION_90), WindowBounds(Rect(0, 0, naturalX, naturalY), naturalInsets, Surface.ROTATION_180), @@ -256,7 +275,7 @@ abstract class AbstractDeviceProfileTest { } private fun initializeCommonVars( - perDisplayBoundsCache: Map<CachedDisplayInfo, Array<WindowBounds>>, + perDisplayBoundsCache: Map<CachedDisplayInfo, List<WindowBounds>>, displayInfo: CachedDisplayInfo, rotation: Int, isGestureMode: Boolean = true, @@ -287,4 +306,27 @@ abstract class AbstractDeviceProfileTest { whenever(displayController.info).thenReturn(info) whenever(displayController.isTransientTaskbar).thenReturn(isGestureMode) } + + /** Create a new dump of DeviceProfile, saves to a file in the device and returns it */ + protected fun dump(context: Context, dp: DeviceProfile, fileName: String): String { + val stringWriter = StringWriter() + PrintWriter(stringWriter).use { dp.dump(context, "", it) } + return stringWriter.toString().also { content -> writeToDevice(context, fileName, content) } + } + + /** Read a file from assets/ and return it as a string */ + protected fun readDumpFromAssets(context: Context, fileName: String): String = + context.assets.open("dumpTests/$fileName").bufferedReader().use(BufferedReader::readText) + + private fun writeToDevice(context: Context, fileName: String, content: String) { + File(context.getDir("dumpTests", Context.MODE_PRIVATE), fileName).writeText(content) + } + + protected fun Float.dpToPx(): Float { + return ResourceUtils.pxFromDp(this, context!!.resources.displayMetrics).toFloat() + } + + protected fun Int.dpToPx(): Int { + return ResourceUtils.pxFromDp(this.toFloat(), context!!.resources.displayMetrics) + } } diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java index 3c2b49ac3b..ff667e60bf 100644 --- a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java +++ b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java @@ -103,6 +103,8 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { public static final char IGNORE = 'x'; // The cells marked by this will be filled by app icons public static final char ICON = 'i'; + // The cells marked by FOLDER will be filled by folders with 27 app icons inside + public static final char FOLDER = 'Z'; // Empty space public static final char EMPTY = '-'; // Widget that will be saved as "main widget" for easier retrieval @@ -139,9 +141,14 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { return this.mType == CellType.IGNORE; } + boolean contains(int x, int y) { + return mBounds.contains(x, y); + } + @Override public String toString() { - return "WidgetRect type = " + mType + " bounds = " + mBounds.toString(); + return "WidgetRect type = " + mType + " x = " + getCellX() + " | y " + getCellY() + + " xs = " + getSpanX() + " ys = " + getSpanY(); } } @@ -171,6 +178,25 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { } } + public static class FolderPoint { + public Point coord; + public char mType; + + public FolderPoint(Point coord, char type) { + this.coord = coord; + mType = type; + } + + /** + * [A-Z]: Represents a folder and number of icons in the folder is represented by + * the order of letter in the alphabet, A=2, B=3, C=4 ... etc. + */ + public int getNumberIconsInside() { + return (mType - 'A') + 2; + } + } + + private HashSet<Character> mUsedWidgetTypes = new HashSet<>(); static final int INFINITE = 99999; @@ -181,6 +207,7 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { Map<Character, WidgetRect> mWidgetsMap = new HashMap<>(); List<IconPoint> mIconPoints = new ArrayList<>(); + List<FolderPoint> mFolderPoints = new ArrayList<>(); WidgetRect mMain = null; @@ -205,6 +232,17 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { } } + public boolean pointInsideRect(int x, int y, WidgetRect rect) { + Boolean isXInRect = x >= rect.getCellX() && x < rect.getCellX() + rect.getSpanX(); + Boolean isYInRect = y >= rect.getCellY() && y < rect.getCellY() + rect.getSpanY(); + return isXInRect && isYInRect; + } + + public WidgetRect getWidgetAt(int x, int y) { + return mWidgetsRects.stream() + .filter(widgetRect -> pointInsideRect(x, y, widgetRect)).findFirst().orElse(null); + } + public List<WidgetRect> getWidgets() { return mWidgetsRects; } @@ -213,6 +251,10 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { return mIconPoints; } + public List<FolderPoint> getFolders() { + return mFolderPoints; + } + public WidgetRect getMain() { return mMain; } @@ -248,6 +290,17 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { } return true; }).collect(Collectors.toList()); + + // Remove overlapping folders and remove them from the board + mFolderPoints = mFolderPoints.stream().filter(folderPoint -> { + int x = folderPoint.coord.x; + int y = folderPoint.coord.y; + if (rect.contains(x, y)) { + mWidget[x][y] = '-'; + return false; + } + return true; + }).collect(Collectors.toList()); } private void removeOverlappingItems(Point p) { @@ -269,6 +322,17 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { } return true; }).collect(Collectors.toList()); + + // Remove overlapping folders and remove them from the board + mFolderPoints = mFolderPoints.stream().filter(folderPoint -> { + int x = folderPoint.coord.x; + int y = folderPoint.coord.y; + if (p.x == x && p.y == y) { + mWidget[x][y] = '-'; + return false; + } + return true; + }).collect(Collectors.toList()); } private char getNextWidgetType() { @@ -373,6 +437,18 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { return iconPoints; } + private static List<FolderPoint> getFolderPoints(char[][] board) { + List<FolderPoint> folderPoints = new ArrayList<>(); + for (int x = 0; x < board.length; x++) { + for (int y = 0; y < board[0].length; y++) { + if (isFolder(board[x][y])) { + folderPoints.add(new FolderPoint(new Point(x, y), board[x][y])); + } + } + } + return folderPoints; + } + public static WidgetRect getMainFromList(List<CellLayoutBoard> boards) { for (CellLayoutBoard board : boards) { WidgetRect main = board.getMain(); @@ -383,6 +459,17 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { return null; } + public static WidgetRect getWidgetIn(List<CellLayoutBoard> boards, int x, int y) { + for (CellLayoutBoard board : boards) { + WidgetRect main = board.getWidgetAt(x, y); + if (main != null) { + return main; + } + x -= board.mWidth; + } + return null; + } + public static CellLayoutBoard boardFromString(String boardStr) { String[] lines = boardStr.split("\n"); CellLayoutBoard board = new CellLayoutBoard(); @@ -406,6 +493,7 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { board.mWidgetsMap.put(widgetRect.mType, widgetRect); }); board.mIconPoints = getIconPoints(board.mWidget); + board.mFolderPoints = getFolderPoints(board.mWidget); return board; } diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java b/tests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java index e33a304941..419cb3d52e 100644 --- a/tests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java +++ b/tests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java @@ -63,8 +63,8 @@ public class CellLayoutTestCaseReader { } public static class Board extends TestSection { - Point gridSize; - String board; + public Point gridSize; + public String board; public Board(Point gridSize, String board) { super(State.BOARD); @@ -127,7 +127,7 @@ public class CellLayoutTestCaseReader { } } - List<TestSection> parse() { + public List<TestSection> parse() { List<TestSection> sections = new ArrayList<>(); String[] lines = mTest.split("\n"); Iterator<String> it = Arrays.stream(lines).iterator(); diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java b/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java index 0d2f252b07..86a7bd3df0 100644 --- a/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java +++ b/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java @@ -42,8 +42,8 @@ public class CellLayoutTestUtils { params.getCellX(), params.getCellY(), launcher.getWorkspace().getIdForScreen(cellLayout), CONTAINER_DESKTOP); int screenId = pos.screenId; - if (screenId >= boards.size() - 1) { - boards.add(new CellLayoutBoard()); + for (int j = boards.size(); j <= screenId; j++) { + boards.add(new CellLayoutBoard(cellLayout.getCountX(), cellLayout.getCountY())); } CellLayoutBoard board = boards.get(screenId); // is icon @@ -51,7 +51,7 @@ public class CellLayoutTestUtils { board.addIcon(pos.cellX, pos.cellY); } else { // is widget - board.addWidget(params.getCellX(), params.getCellY(), params.cellHSpan, + board.addWidget(pos.cellX, pos.cellY, params.cellHSpan, params.cellVSpan); } } diff --git a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java index 8ce932d281..bf7a21c32d 100644 --- a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java +++ b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java @@ -15,62 +15,116 @@ */ package com.android.launcher3.celllayout; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static com.android.launcher3.util.TestUtil.runOnExecutorSync; -import android.content.ContentResolver; -import android.content.ContentValues; import android.content.Context; +import androidx.test.uiautomator.UiDevice; + import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; +import com.android.launcher3.model.BgDataModel.Callbacks; +import com.android.launcher3.model.ModelDbController; +import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; +import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.util.ContentWriter; import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; public class FavoriteItemsTransaction { - private ArrayList<ItemInfo> mItemsToSubmit; + private ArrayList<Supplier<ItemInfo>> mItemsToSubmit; private Context mContext; - private ContentResolver mResolver; - public AbstractLauncherUiTest mTest; - public FavoriteItemsTransaction(Context context, AbstractLauncherUiTest test) { + public FavoriteItemsTransaction(Context context) { mItemsToSubmit = new ArrayList<>(); mContext = context; - mResolver = mContext.getContentResolver(); - mTest = test; } - public FavoriteItemsTransaction addItem(ItemInfo itemInfo) { + public FavoriteItemsTransaction addItem(Supplier<ItemInfo> itemInfo) { this.mItemsToSubmit.add(itemInfo); return this; } - public FavoriteItemsTransaction removeLast() { - this.mItemsToSubmit.remove(this.mItemsToSubmit.size() - 1); - return this; - } - /** * Commits all the ItemInfo into the database of Favorites **/ - public void commit() throws ExecutionException, InterruptedException { - List<ContentValues> values = new ArrayList<>(); - for (ItemInfo item : this.mItemsToSubmit) { - ContentWriter writer = new ContentWriter(mContext); - item.onAddToDatabase(writer); - writer.put(LauncherSettings.Favorites._ID, item.id); - values.add(writer.getValues(mContext)); - } - // Submit the icons to the database in the model thread to prevent race conditions - MODEL_EXECUTOR.submit(() -> mResolver.bulkInsert(LauncherSettings.Favorites.CONTENT_URI, - values.toArray(new ContentValues[0]))).get(); - // Reload the state of the Launcher - MAIN_EXECUTOR.submit(() -> LauncherAppState.getInstance( - mContext).getModel().forceReload()).get(); + public void commit() { + LauncherModel model = LauncherAppState.getInstance(mContext).getModel(); + // Load the model once so that there is no pending migration: + loadModelSync(model); + + runOnExecutorSync(MODEL_EXECUTOR, () -> { + ModelDbController controller = model.getModelDbController(); + // Migrate any previous data so that the DB state is correct + controller.tryMigrateDB(); + + // Create DB again to load fresh data + controller.createEmptyDB(); + controller.clearEmptyDbFlag(); + + // Add new data + try (SQLiteTransaction transaction = controller.newTransaction()) { + int count = mItemsToSubmit.size(); + ArrayList<ItemInfo> containerItems = new ArrayList<>(); + for (int i = 0; i < count; i++) { + ContentWriter writer = new ContentWriter(mContext); + ItemInfo item = mItemsToSubmit.get(i).get(); + + if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { + FolderInfo folderInfo = (FolderInfo) item; + for (ItemInfo itemInfo : folderInfo.contents) { + itemInfo.container = i; + containerItems.add(itemInfo); + } + } + + item.onAddToDatabase(writer); + writer.put(LauncherSettings.Favorites._ID, i); + controller.insert(TABLE_NAME, writer.getValues(mContext)); + } + + for (int i = 0; i < containerItems.size(); i++) { + ContentWriter writer = new ContentWriter(mContext); + ItemInfo item = containerItems.get(i); + item.onAddToDatabase(writer); + writer.put(LauncherSettings.Favorites._ID, count + i); + controller.insert(TABLE_NAME, writer.getValues(mContext)); + } + transaction.commit(); + } + }); + + // Reload model + runOnExecutorSync(MAIN_EXECUTOR, model::forceReload); + loadModelSync(model); + } + + private void loadModelSync(LauncherModel model) { + Callbacks mockCb = new Callbacks() { }; + runOnExecutorSync(MAIN_EXECUTOR, () -> model.addCallbacksAndLoad(mockCb)); + runOnExecutorSync(MODEL_EXECUTOR, () -> { }); + + runOnExecutorSync(MAIN_EXECUTOR, () -> { }); + runOnExecutorSync(MAIN_EXECUTOR, () -> model.removeCallbacks(mockCb)); + } + + /** + * Commits the transaction and waits for home load + */ + public void commitAndLoadHome(LauncherInstrumentation inst) { + commit(); + + // Launch the home activity + UiDevice.getInstance(getInstrumentation()).pressHome(); + inst.waitForLauncherInitialized(); } } diff --git a/tests/src/com/android/launcher3/celllayout/MultipleCellLayoutsSimpleReorder.java b/tests/src/com/android/launcher3/celllayout/MultipleCellLayoutsSimpleReorder.java deleted file mode 100644 index 706c1a7187..0000000000 --- a/tests/src/com/android/launcher3/celllayout/MultipleCellLayoutsSimpleReorder.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.celllayout.testcases; - -import android.graphics.Point; - -import com.android.launcher3.celllayout.ReorderTestCase; - -import java.util.Map; - -/** - * The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character - * in the board mean refer to {@code CellType} - */ -public class MultipleCellLayoutsSimpleReorder { - - /** 5x5 Test - **/ - private static final String START_BOARD_STR_5x5 = "" - + "xxxxx|-----\n" - + "--mm-|-----\n" - + "--mm-|-----\n" - + "-----|-----\n" - + "-----|-----"; - private static final Point MOVE_TO_5x5 = new Point(8, 3); - private static final String END_BOARD_STR_5x5 = "" - + "xxxxx|-----\n" - + "-----|-----\n" - + "-----|-----\n" - + "-----|---mm\n" - + "-----|---mm"; - private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5, - MOVE_TO_5x5, - END_BOARD_STR_5x5); - - /** 4x4 Test - **/ - private static final String START_BOARD_STR_4x4 = "" - + "xxxx|----\n" - + "--mm|----\n" - + "--mm|----\n" - + "----|----"; - private static final Point MOVE_TO_4x4 = new Point(5, 3); - private static final String END_BOARD_STR_4x4 = "" - + "xxxx|----\n" - + "----|----\n" - + "----|-mm-\n" - + "----|-mm-"; - private static final ReorderTestCase TEST_CASE_4x4 = new ReorderTestCase(START_BOARD_STR_4x4, - MOVE_TO_4x4, - END_BOARD_STR_4x4); - - - /** 6x5 Test - **/ - private static final String START_BOARD_STR_6x5 = "" - + "xxxxxx|------\n" - + "--m---|------\n" - + "------|------\n" - + "------|------\n" - + "------|------"; - private static final Point MOVE_TO_6x5 = new Point(10, 4); - private static final String END_BOARD_STR_6x5 = "" - + "xxxxxx|------\n" - + "------|------\n" - + "------|------\n" - + "------|------\n" - + "------|----m-"; - private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5, - MOVE_TO_6x5, - END_BOARD_STR_6x5); - - public static final Map<Point, ReorderTestCase> TEST_BY_GRID_SIZE = - Map.of(new Point(5, 5), TEST_CASE_5x5, - new Point(4, 4), TEST_CASE_4x4, - new Point(6, 5), TEST_CASE_6x5); -} diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java index ee05fe6a9f..91a0634f3d 100644 --- a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java +++ b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java @@ -164,7 +164,7 @@ public class ReorderAlgorithmUnitTest { Context c = new ActivityContextWrapper(getApplicationContext()); DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(c).getDeviceProfile(c).copy(c); mPrevNumColumns = dp.inv.numColumns; - mPrevNumRows = dp.inv.numColumns; + mPrevNumRows = dp.inv.numRows; } @After diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java index 243edc3c58..00d7ce65e3 100644 --- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java +++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java @@ -18,20 +18,24 @@ package com.android.launcher3.celllayout; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import android.content.ContentResolver; +import android.content.ContentValues; import android.graphics.Point; +import android.net.Uri; import android.util.Log; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.celllayout.testcases.MultipleCellLayoutsSimpleReorder; +import com.android.launcher3.MultipageCellLayout; import com.android.launcher3.tapl.Widget; import com.android.launcher3.tapl.WidgetResizeFrame; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TaplTestsLauncher3; import com.android.launcher3.util.rule.ShellCommandRule; +import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Rule; @@ -44,7 +48,6 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutionException; @SmallTest @RunWith(AndroidJUnit4.class) @@ -55,13 +58,14 @@ public class ReorderWidgets extends AbstractLauncherUiTest { private static final String TAG = ReorderWidgets.class.getSimpleName(); + private static final List<String> FOLDABLE_GRIDS = List.of("normal", "practical", "reasonable"); + TestWorkspaceBuilder mWorkspaceBuilder; @Before public void setup() throws Throwable { - mWorkspaceBuilder = new TestWorkspaceBuilder(this, mTargetContext); + mWorkspaceBuilder = new TestWorkspaceBuilder(mTargetContext); TaplTestsLauncher3.initialize(this); - clearHomescreen(); } /** @@ -102,20 +106,63 @@ public class ReorderWidgets extends AbstractLauncherUiTest { return getFromLauncher(CellLayoutTestUtils::workspaceToBoards); } - private void runTestCase(ReorderTestCase testCase) - throws ExecutionException, InterruptedException { + private CellLayoutBoard.WidgetRect getWidgetClosestTo(Point point) { + ArrayList<CellLayoutBoard> workspaceBoards = workspaceToBoards(); + int maxDistance = 9999; + CellLayoutBoard.WidgetRect bestRect = null; + for (int i = 0; i < workspaceBoards.get(0).getWidgets().size(); i++) { + CellLayoutBoard.WidgetRect widget = workspaceBoards.get(0).getWidgets().get(i); + if (widget.getCellX() == 0 && widget.getCellY() == 0) { + continue; + } + int distance = Math.abs(point.x - widget.getCellX()) + + Math.abs(point.y - widget.getCellY()); + if (distance == 0) { + break; + } + if (distance < maxDistance) { + maxDistance = distance; + bestRect = widget; + } + } + return bestRect; + } + + /** + * This function might be odd, its function is to select a widget and leave it in its place. + * The idea is to make the test broader and also test after a widgets resized because the + * underlying code does different things in that case + */ + private void triggerWidgetResize(ReorderTestCase testCase) { + CellLayoutBoard.WidgetRect widgetRect = getWidgetClosestTo(testCase.moveMainTo); + if (widgetRect == null) { + // Some test doesn't have a widget in the final position, in those cases we will ignore + // them + return; + } + Widget widget = mLauncher.getWorkspace().getWidgetAtCell(widgetRect.getCellX(), + widgetRect.getCellY()); + WidgetResizeFrame resizeFrame = widget.dragWidgetToWorkspace(widgetRect.getCellX(), + widgetRect.getCellY(), widgetRect.getSpanX(), widgetRect.getSpanY()); + resizeFrame.dismiss(); + } + + private void runTestCase(ReorderTestCase testCase) { CellLayoutBoard.WidgetRect mainWidgetCellPos = CellLayoutBoard.getMainFromList( testCase.mStart); FavoriteItemsTransaction transaction = - new FavoriteItemsTransaction(mTargetContext, this); + new FavoriteItemsTransaction(mTargetContext); transaction = buildWorkspaceFromBoards(testCase.mStart, transaction); transaction.commit(); + mLauncher.waitForLauncherInitialized(); // resetLoaderState triggers the launcher to start loading the workspace which allows // waitForLauncherCondition to wait for that condition, otherwise the condition would // always be true and it wouldn't wait for the changes to be applied. - resetLoaderState(); waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading()); + + triggerWidgetResize(testCase); + Widget widget = mLauncher.getWorkspace().getWidgetAtCell(mainWidgetCellPos.getCellX(), mainWidgetCellPos.getCellY()); assertNotNull(widget); @@ -137,41 +184,91 @@ public class ReorderWidgets extends AbstractLauncherUiTest { * * @param testCaseMap map containing all the tests per grid size (Point) */ - private void runTestCaseMap(Map<Point, ReorderTestCase> testCaseMap, String testName) - throws ExecutionException, InterruptedException { + private boolean runTestCaseMap(Map<Point, ReorderTestCase> testCaseMap, String testName) { Point iconGridDimensions = mLauncher.getWorkspace().getIconGridDimensions(); Log.d(TAG, "Running test " + testName + " for grid " + iconGridDimensions); - Assume.assumeTrue( - "The test " + testName + " doesn't support " + iconGridDimensions + " grid layout", - testCaseMap.containsKey(iconGridDimensions)); + if (!testCaseMap.containsKey(iconGridDimensions)) { + Log.d(TAG, "The test " + testName + " doesn't support " + iconGridDimensions + + " grid layout"); + return false; + } runTestCase(testCaseMap.get(iconGridDimensions)); + + return true; + } + + private void runTestCaseMapForAllGrids(Map<Point, ReorderTestCase> testCaseMap, + String testName) { + boolean runAtLeastOnce = false; + for (String grid : FOLDABLE_GRIDS) { + applyGridOption(grid); + mLauncher.waitForLauncherInitialized(); + runAtLeastOnce |= runTestCaseMap(testCaseMap, testName); + } + Assume.assumeTrue("None of the grids are supported", runAtLeastOnce); + } + + private void applyGridOption(Object argValue) { + String testProviderAuthority = mTargetContext.getPackageName() + ".grid_control"; + Uri gridUri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(testProviderAuthority) + .appendPath("default_grid") + .build(); + ContentValues values = new ContentValues(); + values.putObject("name", argValue); + Assert.assertEquals(1, + mTargetContext.getContentResolver().update(gridUri, values, null, null)); } @Test public void simpleReorder() throws Exception { - runTestCaseMap(getTestMap("ReorderWidgets/simple_reorder_case"), "push_reorder_case"); + runTestCaseMap(getTestMap("ReorderWidgets/simple_reorder_case"), + "push_reorder_case"); } @Test public void pushTest() throws Exception { - runTestCaseMap(getTestMap("ReorderWidgets/push_reorder_case"), "push_reorder_case"); + runTestCaseMap(getTestMap("ReorderWidgets/push_reorder_case"), + "push_reorder_case"); } @Test public void fullReorder() throws Exception { - runTestCaseMap(getTestMap("ReorderWidgets/full_reorder_case"), "full_reorder_case"); + runTestCaseMap(getTestMap("ReorderWidgets/full_reorder_case"), + "full_reorder_case"); } @Test public void moveOutReorder() throws Exception { - runTestCaseMap(getTestMap("ReorderWidgets/move_out_reorder_case"), "move_out_reorder_case"); + runTestCaseMap(getTestMap("ReorderWidgets/move_out_reorder_case"), + "move_out_reorder_case"); + } + + @Test + public void multipleCellLayoutsSimpleReorder() throws Exception { + Assume.assumeTrue("Test doesn't support foldables", getFromLauncher( + l -> l.getWorkspace().getScreenWithId(0) instanceof MultipageCellLayout)); + runTestCaseMapForAllGrids(getTestMap("ReorderWidgets/multiple_cell_layouts_simple_reorder"), + "multiple_cell_layouts_simple_reorder"); + } + + @Test + public void multipleCellLayoutsNoSpaceReorder() throws Exception { + Assume.assumeTrue("Test doesn't support foldables", getFromLauncher( + l -> l.getWorkspace().getScreenWithId(0) instanceof MultipageCellLayout)); + runTestCaseMapForAllGrids( + getTestMap("ReorderWidgets/multiple_cell_layouts_no_space_reorder"), + "multiple_cell_layouts_no_space_reorder"); } @Test - public void multipleCellLayoutsSimpleReorder() throws ExecutionException, InterruptedException { - Assume.assumeTrue("Test doesn't support foldables", !mLauncher.isTwoPanels()); - runTestCaseMap(MultipleCellLayoutsSimpleReorder.TEST_BY_GRID_SIZE, - MultipleCellLayoutsSimpleReorder.class.getSimpleName()); + public void multipleCellLayoutsReorderToOtherSide() throws Exception { + Assume.assumeTrue("Test doesn't support foldables", getFromLauncher( + l -> l.getWorkspace().getScreenWithId(0) instanceof MultipageCellLayout)); + runTestCaseMapForAllGrids( + getTestMap("ReorderWidgets/multiple_cell_layouts_reorder_other_side"), + "multiple_cell_layouts_reorder_other_side"); } private void addTestCase(Iterator<CellLayoutTestCaseReader.TestSection> sections, @@ -184,7 +281,7 @@ public class ReorderWidgets extends AbstractLauncherUiTest { ((CellLayoutTestCaseReader.Board) sections.next()); Point moveTo = new Point(Integer.parseInt(point.arguments[0]), Integer.parseInt(point.arguments[1])); - testCaseMap.put(startBoard.gridSize, + testCaseMap.put(endBoard.gridSize, new ReorderTestCase(startBoard.board, moveTo, endBoard.board)); } diff --git a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java index 5945605daf..6489bc1cfc 100644 --- a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java +++ b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java @@ -15,6 +15,10 @@ */ package com.android.launcher3.celllayout; +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.launcher3.ui.TestViewHelpers.findWidgetProvider; import static com.android.launcher3.util.WidgetUtils.createWidgetInfo; import android.content.ComponentName; @@ -25,33 +29,30 @@ import android.os.Process; import android.os.UserHandle; import android.util.Log; -import androidx.test.core.app.ApplicationProvider; - import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherSettings; import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; -import com.android.launcher3.ui.AbstractLauncherUiTest; -import com.android.launcher3.ui.TestViewHelpers; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; +import java.util.function.Supplier; +import java.util.stream.IntStream; + public class TestWorkspaceBuilder { private static final String TAG = "CellLayoutBoardBuilder"; - private static final ComponentName APP_COMPONENT_NAME = new ComponentName( + private static final String TEST_ACTIVITY_PACKAGE_PREFIX = "com.android.launcher3.tests."; + private ComponentName mAppComponentName = new ComponentName( "com.google.android.calculator", "com.android.calculator2.Calculator"); - - public AbstractLauncherUiTest mTest; - private UserHandle mMyUser; private Context mContext; private ContentResolver mResolver; - public TestWorkspaceBuilder(AbstractLauncherUiTest test, Context context) { - mTest = test; + public TestWorkspaceBuilder(Context context) { mMyUser = Process.myUserHandle(); mContext = context; mResolver = mContext.getContentResolver(); @@ -68,10 +69,9 @@ public class TestWorkspaceBuilder { for (int y = initY; y < initY + widgetRect.getSpanY(); y++) { try { // this widgets are filling, we don't care if we can't place them - ItemInfo item = createWidgetInCell( + transaction.addItem(createWidgetInCell( new CellLayoutBoard.WidgetRect(CellLayoutBoard.CellType.IGNORE, - new Rect(x, y, x, y)), screenId); - transaction.addItem(item); + new Rect(x, y, x, y)), screenId)); } catch (Exception e) { Log.d(TAG, "Unable to place filling widget at " + x + "," + y); } @@ -80,15 +80,21 @@ public class TestWorkspaceBuilder { return transaction; } - private int getID() { - return LauncherSettings.Settings.call( - mResolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID) - .getInt(LauncherSettings.Settings.EXTRA_VALUE); + private AppInfo getApp() { + return new AppInfo(mAppComponentName, "test icon", mMyUser, + AppInfo.makeLaunchIntent(mAppComponentName)); } - private AppInfo getApp() { - return new AppInfo(APP_COMPONENT_NAME, "test icon", mMyUser, - AppInfo.makeLaunchIntent(APP_COMPONENT_NAME)); + /** + * Helper to set the app to use for the test workspace, + * using activity-alias from AndroidManifest-common. + * @param testAppName the android:name field of the test app activity-alias to use + */ + public void setTestAppActivityAlias(String testAppName) { + this.mAppComponentName = new ComponentName( + getInstrumentation().getContext().getPackageName(), + TEST_ACTIVITY_PACKAGE_PREFIX + testAppName + ); } private void addCorrespondingWidgetRect(CellLayoutBoard.WidgetRect widgetRect, @@ -108,7 +114,10 @@ public class TestWorkspaceBuilder { board.getWidgets().forEach( (widgetRect) -> addCorrespondingWidgetRect(widgetRect, transaction, screenId)); board.getIcons().forEach((iconPoint) -> - transaction.addItem(createIconInCell(iconPoint, screenId)) + transaction.addItem(() -> createIconInCell(iconPoint, screenId)) + ); + board.getFolders().forEach((folderPoint) -> + transaction.addItem(() -> createFolderInCell(folderPoint, screenId)) ); return transaction; } @@ -118,29 +127,52 @@ public class TestWorkspaceBuilder { * be clean otherwise this doesn't overrides the existing icons. */ public FavoriteItemsTransaction fillHotseatIcons(FavoriteItemsTransaction transaction) { - int hotseatCount = InvariantDeviceProfile.INSTANCE.get(mContext).numDatabaseHotseatIcons; - for (int i = 0; i < hotseatCount; i++) { - transaction.addItem(getHotseatValues(i)); - } + IntStream.range(0, InvariantDeviceProfile.INSTANCE.get(mContext).numDatabaseHotseatIcons) + .forEach(i -> transaction.addItem(() -> getHotseatValues(i))); return transaction; } - private ItemInfo createWidgetInCell(CellLayoutBoard.WidgetRect widgetRect, int screenId) { - LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(mTest, false); - LauncherAppWidgetInfo item = createWidgetInfo(info, - ApplicationProvider.getApplicationContext(), true); - item.id = getID(); - item.cellX = widgetRect.getCellX(); - item.cellY = widgetRect.getCellY(); - item.spanX = widgetRect.getSpanX(); - item.spanY = widgetRect.getSpanY(); + private Supplier<ItemInfo> createWidgetInCell( + CellLayoutBoard.WidgetRect widgetRect, int screenId) { + // Create the widget lazily since the appWidgetId can get lost during setup + return () -> { + LauncherAppWidgetProviderInfo info = findWidgetProvider(false); + LauncherAppWidgetInfo item = createWidgetInfo(info, getApplicationContext(), true); + item.cellX = widgetRect.getCellX(); + item.cellY = widgetRect.getCellY(); + item.spanX = widgetRect.getSpanX(); + item.spanY = widgetRect.getSpanY(); + item.screenId = screenId; + return item; + }; + } + + public FolderInfo createFolderInCell(CellLayoutBoard.FolderPoint folderPoint, int screenId) { + FolderInfo folderInfo = new FolderInfo(); + folderInfo.screenId = screenId; + folderInfo.container = LauncherSettings.Favorites.CONTAINER_DESKTOP; + folderInfo.cellX = folderPoint.coord.x; + folderInfo.cellY = folderPoint.coord.y; + folderInfo.minSpanY = folderInfo.minSpanX = folderInfo.spanX = folderInfo.spanY = 1; + folderInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, null); + + for (int i = 0; i < folderPoint.getNumberIconsInside(); i++) { + folderInfo.add(getDefaultWorkspaceItem(screenId), false); + } + + return folderInfo; + } + + private WorkspaceItemInfo getDefaultWorkspaceItem(int screenId) { + WorkspaceItemInfo item = new WorkspaceItemInfo(getApp()); item.screenId = screenId; + item.minSpanY = item.minSpanX = item.spanX = item.spanY = 1; + item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP; return item; } private ItemInfo createIconInCell(CellLayoutBoard.IconPoint iconPoint, int screenId) { WorkspaceItemInfo item = new WorkspaceItemInfo(getApp()); - item.id = getID(); item.screenId = screenId; item.cellX = iconPoint.getCoord().x; item.cellY = iconPoint.getCoord().y; @@ -151,7 +183,6 @@ public class TestWorkspaceBuilder { private ItemInfo getHotseatValues(int x) { WorkspaceItemInfo item = new WorkspaceItemInfo(getApp()); - item.id = getID(); item.cellX = x; item.cellY = 0; item.minSpanY = item.minSpanX = item.spanX = item.spanY = 1; diff --git a/tests/src/com/android/launcher3/folder/PreviewBackgroundTest.java b/tests/src/com/android/launcher3/folder/PreviewBackgroundTest.java new file mode 100644 index 0000000000..715a1f8c70 --- /dev/null +++ b/tests/src/com/android/launcher3/folder/PreviewBackgroundTest.java @@ -0,0 +1,323 @@ +/* + * 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.launcher3.folder; + +import static com.android.launcher3.folder.PreviewBackground.ACCEPT_SCALE_FACTOR; +import static com.android.launcher3.folder.PreviewBackground.CONSUMPTION_ANIMATION_DURATION; +import static com.android.launcher3.folder.PreviewBackground.HOVER_ANIMATION_DURATION; +import static com.android.launcher3.folder.PreviewBackground.HOVER_SCALE; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.PathInterpolator; + +import androidx.test.filters.SmallTest; + +import com.android.launcher3.CellLayout; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class PreviewBackgroundTest { + + private static final float REST_SCALE = 1f; + private static final float EPSILON = 0.00001f; + + @Mock + CellLayout mCellLayout; + + private final PreviewBackground mPreviewBackground = new PreviewBackground(); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mPreviewBackground.mScale = REST_SCALE; + mPreviewBackground.mIsAccepting = false; + mPreviewBackground.mIsHovered = false; + mPreviewBackground.mIsHoveredOrAnimating = false; + mPreviewBackground.invalidate(); + } + + @Test + public void testAnimateScale_restToHovered() { + mPreviewBackground.setHovered(true); + runAnimationToFraction(1f); + + assertEquals("Scale not changed.", mPreviewBackground.mScale, HOVER_SCALE, EPSILON); + assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(), + HOVER_ANIMATION_DURATION); + assertTrue("Wrong interpolator used.", + mPreviewBackground.mScaleAnimator.getInterpolator() instanceof PathInterpolator); + endAnimation(); + assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0, + EPSILON); + } + + @Test + public void testAnimateScale_restToNotHovered() { + mPreviewBackground.setHovered(false); + + assertEquals("Scale changed.", mPreviewBackground.mScale, REST_SCALE, EPSILON); + assertNull("Animator not null.", mPreviewBackground.mScaleAnimator); + assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0, + EPSILON); + } + + @Test + public void testAnimateScale_hoveredToHovered() { + mPreviewBackground.mScale = HOVER_SCALE; + mPreviewBackground.mIsHovered = true; + mPreviewBackground.mIsHoveredOrAnimating = true; + mPreviewBackground.invalidate(); + + mPreviewBackground.setHovered(true); + + assertEquals("Scale changed.", mPreviewBackground.mScale, HOVER_SCALE, EPSILON); + assertNull("Animator not null.", mPreviewBackground.mScaleAnimator); + assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0, + EPSILON); + } + + @Test + public void testAnimateScale_hoveredToRest() { + mPreviewBackground.mScale = HOVER_SCALE; + mPreviewBackground.mIsHovered = true; + mPreviewBackground.mIsHoveredOrAnimating = true; + mPreviewBackground.invalidate(); + + mPreviewBackground.setHovered(false); + runAnimationToFraction(1f); + + assertEquals("Scale not changed.", mPreviewBackground.mScale, REST_SCALE, EPSILON); + assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(), + HOVER_ANIMATION_DURATION); + assertTrue("Wrong interpolator used.", + mPreviewBackground.mScaleAnimator.getInterpolator() instanceof PathInterpolator); + endAnimation(); + assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0, + EPSILON); + } + + @Test + public void testAnimateScale_restToAccept() { + mPreviewBackground.animateToAccept(mCellLayout, 0, 0); + runAnimationToFraction(1f); + + assertEquals("Scale changed.", mPreviewBackground.mScale, ACCEPT_SCALE_FACTOR, EPSILON); + assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(), + CONSUMPTION_ANIMATION_DURATION); + assertTrue("Wrong interpolator used.", + mPreviewBackground.mScaleAnimator.getInterpolator() + instanceof AccelerateDecelerateInterpolator); + endAnimation(); + assertEquals("Scale progress not 1.", mPreviewBackground.getAcceptScaleProgress(), 1, + EPSILON); + } + + @Test + public void testAnimateScale_restToRest() { + mPreviewBackground.animateToRest(); + + assertEquals("Scale changed.", mPreviewBackground.mScale, REST_SCALE, EPSILON); + assertNull("Animator not null.", mPreviewBackground.mScaleAnimator); + assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0, + EPSILON); + } + + @Test + public void testAnimateScale_acceptToRest() { + mPreviewBackground.mScale = ACCEPT_SCALE_FACTOR; + mPreviewBackground.mIsAccepting = true; + mPreviewBackground.invalidate(); + + mPreviewBackground.animateToRest(); + runAnimationToFraction(1f); + + assertEquals("Scale not changed.", mPreviewBackground.mScale, REST_SCALE, EPSILON); + assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(), + CONSUMPTION_ANIMATION_DURATION); + assertTrue("Wrong interpolator used.", + mPreviewBackground.mScaleAnimator.getInterpolator() + instanceof AccelerateDecelerateInterpolator); + endAnimation(); + assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0, + EPSILON); + } + + @Test + public void testAnimateScale_acceptToHover() { + mPreviewBackground.mScale = ACCEPT_SCALE_FACTOR; + mPreviewBackground.mIsAccepting = true; + mPreviewBackground.invalidate(); + + mPreviewBackground.mIsAccepting = false; + mPreviewBackground.setHovered(true); + runAnimationToFraction(1f); + + assertEquals("Scale not changed.", mPreviewBackground.mScale, HOVER_SCALE, EPSILON); + assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(), + HOVER_ANIMATION_DURATION); + assertTrue("Wrong interpolator used.", + mPreviewBackground.mScaleAnimator.getInterpolator() instanceof PathInterpolator); + endAnimation(); + assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0, + EPSILON); + } + + @Test + public void testAnimateScale_hoverToAccept() { + mPreviewBackground.mScale = HOVER_SCALE; + mPreviewBackground.mIsHovered = true; + mPreviewBackground.mIsHoveredOrAnimating = true; + mPreviewBackground.invalidate(); + + mPreviewBackground.animateToAccept(mCellLayout, 0, 0); + runAnimationToFraction(1f); + + assertEquals("Scale not changed.", mPreviewBackground.mScale, ACCEPT_SCALE_FACTOR, EPSILON); + assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(), + CONSUMPTION_ANIMATION_DURATION); + assertTrue("Wrong interpolator used.", + mPreviewBackground.mScaleAnimator.getInterpolator() + instanceof AccelerateDecelerateInterpolator); + mPreviewBackground.mIsHovered = false; + endAnimation(); + assertEquals("Scale progress not 1.", mPreviewBackground.getAcceptScaleProgress(), 1, + EPSILON); + } + + @Test + public void testAnimateScale_midwayToHoverToAccept() { + mPreviewBackground.setHovered(true); + runAnimationToFraction(0.5f); + assertTrue("Scale not changed.", + mPreviewBackground.mScale > REST_SCALE && mPreviewBackground.mScale < HOVER_SCALE); + assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0, + EPSILON); + + mPreviewBackground.animateToAccept(mCellLayout, 0, 0); + runAnimationToFraction(1f); + + assertEquals("Scale not changed.", mPreviewBackground.mScale, ACCEPT_SCALE_FACTOR, EPSILON); + assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(), + CONSUMPTION_ANIMATION_DURATION); + assertTrue("Wrong interpolator used.", + mPreviewBackground.mScaleAnimator.getInterpolator() + instanceof AccelerateDecelerateInterpolator); + mPreviewBackground.mIsHovered = false; + endAnimation(); + assertEquals("Scale progress not 1.", mPreviewBackground.getAcceptScaleProgress(), 1, + EPSILON); + assertNull("Animator not null.", mPreviewBackground.mScaleAnimator); + } + + @Test + public void testAnimateScale_partWayToAcceptToHover() { + mPreviewBackground.animateToAccept(mCellLayout, 0, 0); + runAnimationToFraction(0.25f); + assertTrue("Scale not changed part way.", mPreviewBackground.mScale > REST_SCALE + && mPreviewBackground.mScale < ACCEPT_SCALE_FACTOR); + + mPreviewBackground.mIsAccepting = false; + mPreviewBackground.setHovered(true); + runAnimationToFraction(1f); + + assertEquals("Scale not changed.", mPreviewBackground.mScale, HOVER_SCALE, EPSILON); + assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(), + HOVER_ANIMATION_DURATION); + assertTrue("Wrong interpolator used.", + mPreviewBackground.mScaleAnimator.getInterpolator() instanceof PathInterpolator); + endAnimation(); + assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0, + EPSILON); + } + + @Test + public void testAnimateScale_midwayToAcceptEqualsHover() { + mPreviewBackground.animateToAccept(mCellLayout, 0, 0); + runAnimationToFraction(0.5f); + assertEquals("Scale not changed.", mPreviewBackground.mScale, HOVER_SCALE, EPSILON); + mPreviewBackground.mIsAccepting = false; + + mPreviewBackground.setHovered(true); + + assertEquals("Scale changed.", mPreviewBackground.mScale, HOVER_SCALE, EPSILON); + assertNull("Animator not null.", mPreviewBackground.mScaleAnimator); + assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0, + EPSILON); + } + + @Test + public void testAnimateScale_midwayToHoverToRest() { + mPreviewBackground.setHovered(true); + runAnimationToFraction(0.5f); + assertTrue("Scale not changed midway.", + mPreviewBackground.mScale > REST_SCALE && mPreviewBackground.mScale < HOVER_SCALE); + + mPreviewBackground.mIsHovered = false; + mPreviewBackground.animateToRest(); + runAnimationToFraction(1f); + + assertEquals("Scale not changed.", mPreviewBackground.mScale, REST_SCALE, EPSILON); + assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(), + HOVER_ANIMATION_DURATION); + assertTrue("Wrong interpolator used.", + mPreviewBackground.mScaleAnimator.getInterpolator() instanceof PathInterpolator); + endAnimation(); + assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0, + EPSILON); + } + + @Test + public void testAnimateScale_midwayToAcceptToRest() { + mPreviewBackground.animateToAccept(mCellLayout, 0, 0); + runAnimationToFraction(0.5f); + assertTrue("Scale not changed.", mPreviewBackground.mScale > REST_SCALE + && mPreviewBackground.mScale < ACCEPT_SCALE_FACTOR); + + mPreviewBackground.animateToRest(); + runAnimationToFraction(1f); + + assertEquals("Scale not changed.", mPreviewBackground.mScale, REST_SCALE, EPSILON); + assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(), + CONSUMPTION_ANIMATION_DURATION); + assertTrue("Wrong interpolator used.", + mPreviewBackground.mScaleAnimator.getInterpolator() + instanceof AccelerateDecelerateInterpolator); + endAnimation(); + assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0, + EPSILON); + } + + private void runAnimationToFraction(float animationFraction) { + mPreviewBackground.mScaleAnimator.setCurrentFraction(animationFraction); + } + + private void endAnimation() { + mPreviewBackground.mScaleAnimator.end(); + } +} diff --git a/tests/src/com/android/launcher3/icons/FastBitmapDrawableTest.java b/tests/src/com/android/launcher3/icons/FastBitmapDrawableTest.java new file mode 100644 index 0000000000..fbbfb2af48 --- /dev/null +++ b/tests/src/com/android/launcher3/icons/FastBitmapDrawableTest.java @@ -0,0 +1,345 @@ +/* + * 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.launcher3.icons; + +import static com.android.launcher3.icons.FastBitmapDrawable.CLICK_FEEDBACK_DURATION; +import static com.android.launcher3.icons.FastBitmapDrawable.HOVERED_SCALE; +import static com.android.launcher3.icons.FastBitmapDrawable.HOVER_FEEDBACK_DURATION; +import static com.android.launcher3.icons.FastBitmapDrawable.PRESSED_SCALE; +import static com.android.launcher3.icons.FastBitmapDrawable.SCALE; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.PathInterpolator; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; + +/** + * Tests for FastBitmapDrawable. + */ +@SmallTest +@UiThreadTest +@RunWith(AndroidJUnit4.class) +public class FastBitmapDrawableTest { + private static final float EPSILON = 0.00001f; + + @Spy + FastBitmapDrawable mFastBitmapDrawable = + spy(new FastBitmapDrawable(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888))); + @Mock Drawable mBadge; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + FastBitmapDrawable.setFlagHoverEnabled(true); + when(mFastBitmapDrawable.isVisible()).thenReturn(true); + mFastBitmapDrawable.mIsPressed = false; + mFastBitmapDrawable.mIsHovered = false; + mFastBitmapDrawable.resetScale(); + } + + @Test + public void testOnStateChange_noState() { + int[] state = new int[]{}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // No scale changes without state change. + assertFalse("State change handled.", isHandled); + assertNull("Scale animation not null.", mFastBitmapDrawable.mScaleAnimation); + } + + @Test + public void testOnStateChange_statePressed() { + int[] state = new int[]{android.R.attr.state_pressed}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // Animate to state pressed. + assertTrue("State change not handled.", isHandled); + assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(), + CLICK_FEEDBACK_DURATION); + mFastBitmapDrawable.mScaleAnimation.end(); + assertEquals("End value not correct.", + (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON); + assertTrue("Wrong interpolator used.", + mFastBitmapDrawable.mScaleAnimation.getInterpolator() + instanceof AccelerateInterpolator); + } + + @Test + public void testOnStateChange_stateHovered() { + int[] state = new int[]{android.R.attr.state_hovered}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // Animate to state hovered. + assertTrue("State change not handled.", isHandled); + assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(), + HOVER_FEEDBACK_DURATION); + mFastBitmapDrawable.mScaleAnimation.end(); + assertEquals("End value not correct.", + (float) SCALE.get(mFastBitmapDrawable), HOVERED_SCALE, EPSILON); + assertTrue("Wrong interpolator used.", + mFastBitmapDrawable.mScaleAnimation.getInterpolator() instanceof PathInterpolator); + } + + @Test + public void testOnStateChange_stateHoveredFlagDisabled() { + FastBitmapDrawable.setFlagHoverEnabled(false); + int[] state = new int[]{android.R.attr.state_hovered}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // No state change with flag disabled. + assertFalse("Hover state change handled with flag disabled.", isHandled); + assertNull("Animation should not run with hover flag disabled.", + mFastBitmapDrawable.mScaleAnimation); + } + + @Test + public void testOnStateChange_statePressedAndHovered() { + int[] state = new int[]{android.R.attr.state_pressed, android.R.attr.state_hovered}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // Animate to pressed state only. + assertTrue("State change not handled.", isHandled); + assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(), + CLICK_FEEDBACK_DURATION); + mFastBitmapDrawable.mScaleAnimation.end(); + assertEquals("End value not correct.", + (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON); + assertTrue("Wrong interpolator used.", + mFastBitmapDrawable.mScaleAnimation.getInterpolator() + instanceof AccelerateInterpolator); + } + + @Test + public void testOnStateChange_stateHoveredAndPressed() { + int[] state = new int[]{android.R.attr.state_hovered, android.R.attr.state_pressed}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // Animate to pressed state only. + assertTrue("State change not handled.", isHandled); + assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(), + CLICK_FEEDBACK_DURATION); + mFastBitmapDrawable.mScaleAnimation.end(); + assertEquals("End value not correct.", + (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON); + assertTrue("Wrong interpolator used.", + mFastBitmapDrawable.mScaleAnimation.getInterpolator() + instanceof AccelerateInterpolator); + } + + @Test + public void testOnStateChange_stateHoveredAndPressedToPressed() { + mFastBitmapDrawable.mIsPressed = true; + mFastBitmapDrawable.mIsHovered = true; + SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE); + int[] state = new int[]{android.R.attr.state_pressed}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // No scale change from pressed state to pressed state. + assertTrue("State not changed.", isHandled); + assertEquals("End value not correct.", + (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON); + } + + @Test + public void testOnStateChange_stateHoveredAndPressedToHovered() { + mFastBitmapDrawable.mIsPressed = true; + mFastBitmapDrawable.mIsHovered = true; + SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE); + int[] state = new int[]{android.R.attr.state_hovered}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // No scale change from pressed state to hovered state. + assertTrue("State not changed.", isHandled); + assertEquals("End value not correct.", + (float) SCALE.get(mFastBitmapDrawable), HOVERED_SCALE, EPSILON); + } + + @Test + public void testOnStateChange_stateHoveredToPressed() { + mFastBitmapDrawable.mIsHovered = true; + SCALE.setValue(mFastBitmapDrawable, HOVERED_SCALE); + int[] state = new int[]{android.R.attr.state_pressed}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // No scale change from pressed state to hovered state. + assertTrue("State not changed.", isHandled); + assertEquals("End value not correct.", + (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON); + } + + @Test + public void testOnStateChange_statePressedToHovered() { + mFastBitmapDrawable.mIsPressed = true; + SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE); + int[] state = new int[]{android.R.attr.state_hovered}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // No scale change from pressed state to hovered state. + assertTrue("State not changed.", isHandled); + assertEquals("End value not correct.", + (float) SCALE.get(mFastBitmapDrawable), HOVERED_SCALE, EPSILON); + } + + @Test + public void testOnStateChange_stateDefaultFromPressed() { + mFastBitmapDrawable.mIsPressed = true; + SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE); + int[] state = new int[]{}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // Animate to default state from pressed state. + assertTrue("State change not handled.", isHandled); + assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(), + CLICK_FEEDBACK_DURATION); + mFastBitmapDrawable.mScaleAnimation.end(); + assertEquals("End value not correct.", (float) SCALE.get(mFastBitmapDrawable), 1f, EPSILON); + assertTrue("Wrong interpolator used.", + mFastBitmapDrawable.mScaleAnimation.getInterpolator() + instanceof DecelerateInterpolator); + } + + @Test + public void testOnStateChange_stateDefaultFromHovered() { + mFastBitmapDrawable.mIsHovered = true; + SCALE.setValue(mFastBitmapDrawable, HOVERED_SCALE); + int[] state = new int[]{}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // Animate to default state from hovered state. + assertTrue("State change not handled.", isHandled); + assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(), + HOVER_FEEDBACK_DURATION); + mFastBitmapDrawable.mScaleAnimation.end(); + assertEquals("End value not correct.", (float) SCALE.get(mFastBitmapDrawable), 1f, EPSILON); + assertTrue("Wrong interpolator used.", + mFastBitmapDrawable.mScaleAnimation.getInterpolator() instanceof PathInterpolator); + } + + @Test + public void testOnStateChange_stateHoveredWhilePartiallyScaled() { + SCALE.setValue(mFastBitmapDrawable, 0.5f); + int[] state = new int[]{android.R.attr.state_hovered}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // Animate to hovered state from midway to pressed state. + assertTrue("State change not handled.", isHandled); + assertEquals("Duration not correct.", + mFastBitmapDrawable.mScaleAnimation.getDuration(), HOVER_FEEDBACK_DURATION); + mFastBitmapDrawable.mScaleAnimation.end(); + assertEquals("End value not correct.", + (float) SCALE.get(mFastBitmapDrawable), HOVERED_SCALE, EPSILON); + assertTrue("Wrong interpolator used.", + mFastBitmapDrawable.mScaleAnimation.getInterpolator() instanceof PathInterpolator); + } + + @Test + public void testOnStateChange_statePressedWhilePartiallyScaled() { + SCALE.setValue(mFastBitmapDrawable, 0.5f); + int[] state = new int[]{android.R.attr.state_pressed}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // Animate to pressed state from midway to hovered state. + assertTrue("State change not handled.", isHandled); + assertEquals("Duration not correct.", + mFastBitmapDrawable.mScaleAnimation.getDuration(), CLICK_FEEDBACK_DURATION); + mFastBitmapDrawable.mScaleAnimation.end(); + assertEquals("End value not correct.", + (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON); + assertTrue("Wrong interpolator used.", + mFastBitmapDrawable.mScaleAnimation.getInterpolator() + instanceof AccelerateInterpolator); + } + + @Test + public void testOnStateChange_stateDefaultFromPressedNotVisible() { + when(mFastBitmapDrawable.isVisible()).thenReturn(false); + mFastBitmapDrawable.mIsPressed = true; + SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE); + clearInvocations(mFastBitmapDrawable); + int[] state = new int[]{}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // No animations when state was pressed but drawable no longer visible. Set values directly. + assertTrue("State change not handled.", isHandled); + assertNull("Scale animation not null.", mFastBitmapDrawable.mScaleAnimation); + assertEquals("End value not correct.", (float) SCALE.get(mFastBitmapDrawable), 1f, EPSILON); + verify(mFastBitmapDrawable).invalidateSelf(); + } + + @Test + public void testOnStateChange_stateDefaultFromHoveredNotVisible() { + when(mFastBitmapDrawable.isVisible()).thenReturn(false); + mFastBitmapDrawable.mIsHovered = true; + SCALE.setValue(mFastBitmapDrawable, HOVERED_SCALE); + clearInvocations(mFastBitmapDrawable); + int[] state = new int[]{}; + + boolean isHandled = mFastBitmapDrawable.onStateChange(state); + + // No animations when state was hovered but drawable no longer visible. Set values directly. + assertTrue("State change not handled.", isHandled); + assertNull("Scale animation not null.", mFastBitmapDrawable.mScaleAnimation); + assertEquals("End value not correct.", (float) SCALE.get(mFastBitmapDrawable), 1f, EPSILON); + verify(mFastBitmapDrawable).invalidateSelf(); + } + + @Test + public void testUpdateBadgeAlpha() { + mFastBitmapDrawable.setBadge(mBadge); + + mFastBitmapDrawable.setAlpha(1); + mFastBitmapDrawable.setAlpha(0); + + verify(mBadge).setAlpha(1); + verify(mBadge).setAlpha(0); + } +} diff --git a/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt b/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt index 03352fe234..98191fecf0 100644 --- a/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt +++ b/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt @@ -17,17 +17,18 @@ package com.android.launcher3.model import android.content.ComponentName import android.content.Context -import android.content.Intent import android.graphics.Rect import com.android.launcher3.InvariantDeviceProfile import com.android.launcher3.LauncherAppState -import com.android.launcher3.LauncherSettings +import com.android.launcher3.model.data.AppInfo import com.android.launcher3.model.data.WorkspaceItemInfo -import com.android.launcher3.util.ContentWriter import com.android.launcher3.util.GridOccupancy import com.android.launcher3.util.IntArray import com.android.launcher3.util.IntSparseArrayMap +import com.android.launcher3.util.LauncherLayoutBuilder import com.android.launcher3.util.LauncherModelHelper +import com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY +import com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE import java.util.UUID /** Base class for workspace related tests. */ @@ -38,6 +39,7 @@ abstract class AbstractWorkspaceModelTest { val nonEmptyScreenSpaces = listOf(Rect(1, 2, 3, 4)) } + protected lateinit var mLayoutBuilder: LauncherLayoutBuilder protected lateinit var mTargetContext: Context protected lateinit var mIdp: InvariantDeviceProfile protected lateinit var mAppState: LauncherAppState @@ -47,6 +49,7 @@ abstract class AbstractWorkspaceModelTest { protected lateinit var mScreenOccupancy: IntSparseArrayMap<GridOccupancy> open fun setup() { + mLayoutBuilder = LauncherLayoutBuilder() mModelHelper = LauncherModelHelper() mTargetContext = mModelHelper.sandboxContext mIdp = InvariantDeviceProfile.INSTANCE[mTargetContext] @@ -64,10 +67,11 @@ abstract class AbstractWorkspaceModelTest { /** Sets up workspaces with the given screen IDs with some items and a 2x2 space. */ fun setupWorkspaces(screenIdsWithItems: List<Int>) { - var nextItemId = 1 - screenIdsWithItems.forEach { screenId -> - nextItemId = setupWorkspace(nextItemId, screenId, nonEmptyScreenSpaces) - } + screenIdsWithItems.forEach { screenId -> setupWorkspace(screenId, nonEmptyScreenSpaces) } + mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder) + mIdp.numRows = 5 + mIdp.numColumns = mIdp.numRows + mModelHelper.loadModelSync() } /** @@ -78,30 +82,23 @@ abstract class AbstractWorkspaceModelTest { screen1: List<Rect>? = null, screen2: List<Rect>? = null, screen3: List<Rect>? = null, - ) = listOf(screen0, screen1, screen2, screen3).let(this::setupWithSpaces) + ) { + listOf(screen0, screen1, screen2, screen3).let(this::setupWithSpaces) + mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder) + mIdp.numRows = 5 + mIdp.numColumns = mIdp.numRows + mModelHelper.loadModelSync() + } private fun setupWithSpaces(workspaceSpaces: List<List<Rect>?>) { - var nextItemId = 1 workspaceSpaces.forEachIndexed { screenId, spaces -> if (spaces != null) { - nextItemId = setupWorkspace(nextItemId, screenId, spaces) + setupWorkspace(screenId, spaces) } } } - private fun setupWorkspace(startId: Int, screenId: Int, spaces: List<Rect>): Int { - return mModelHelper.executeSimpleTask { dataModel -> - writeWorkspaceWithSpaces(dataModel, startId, screenId, spaces) - } - } - - private fun writeWorkspaceWithSpaces( - bgDataModel: BgDataModel, - itemStartId: Int, - screenId: Int, - spaces: List<Rect>, - ): Int { - var itemId = itemStartId + private fun setupWorkspace(screenId: Int, spaces: List<Rect>) { val occupancy = GridOccupancy(mIdp.numColumns, mIdp.numRows) occupancy.markCells(0, 0, mIdp.numColumns, mIdp.numRows, true) spaces.forEach { spaceRect -> occupancy.markCells(spaceRect, false) } @@ -109,35 +106,22 @@ abstract class AbstractWorkspaceModelTest { mScreenOccupancy.append(screenId, occupancy) for (x in 0 until mIdp.numColumns) { for (y in 0 until mIdp.numRows) { - if (!occupancy.cells[x][y]) { - continue + if (occupancy.cells[x][y]) { + mLayoutBuilder.atWorkspace(x, y, screenId).putApp(TEST_PACKAGE, TEST_ACTIVITY) } - val info = getExistingItem() - info.id = itemId++ - info.screenId = screenId - info.cellX = x - info.cellY = y - info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP - bgDataModel.addItem(mTargetContext, info, false) - val writer = ContentWriter(mTargetContext) - info.writeToValues(writer) - writer.put(LauncherSettings.Favorites._ID, info.id) - mTargetContext.contentResolver.insert( - LauncherSettings.Favorites.CONTENT_URI, - writer.getValues(mTargetContext) - ) } } - return itemId } fun getExistingItem() = - WorkspaceItemInfo().apply { intent = Intent().setComponent(ComponentName("a", "b")) } + WorkspaceItemInfo().apply { + intent = AppInfo.makeLaunchIntent(ComponentName(TEST_PACKAGE, TEST_ACTIVITY)) + } fun getNewItem(): WorkspaceItemInfo { val itemPackage = UUID.randomUUID().toString() return WorkspaceItemInfo().apply { - intent = Intent().setComponent(ComponentName(itemPackage, itemPackage)) + intent = AppInfo.makeLaunchIntent(ComponentName(itemPackage, itemPackage)) } } } diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt index 6636b8a31a..115522721a 100644 --- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt +++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt @@ -23,7 +23,7 @@ import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.model.data.WorkspaceItemInfo import com.android.launcher3.util.Executors import com.android.launcher3.util.IntArray -import com.android.launcher3.util.IntSet +import com.android.launcher3.util.TestUtil.runOnExecutorSync import com.android.launcher3.util.any import com.android.launcher3.util.eq import com.android.launcher3.util.same @@ -32,8 +32,6 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.times import org.mockito.Mockito.verify @@ -46,11 +44,7 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidJUnit4::class) class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { - @Captor private lateinit var mAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>> - - @Captor private lateinit var mNotAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>> - - @Mock private lateinit var mDataModelCallbacks: BgDataModel.Callbacks + private lateinit var mDataModelCallbacks: MyCallbacks @Mock private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder @@ -58,7 +52,7 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { override fun setup() { super.setup() MockitoAnnotations.initMocks(this) - whenever(mDataModelCallbacks.getPagesToBindSynchronously(any())).thenReturn(IntSet()) + mDataModelCallbacks = MyCallbacks() Executors.MAIN_EXECUTOR.submit { mModelHelper.model.addCallbacks(mDataModelCallbacks) } .get() } @@ -105,7 +99,7 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { val addedItems = testAddItems(nonEmptyScreenIds, itemToAdd) assertThat(addedItems.size).isEqualTo(0) - verifyZeroInteractions(mWorkspaceItemSpaceFinder, mDataModelCallbacks) + verifyZeroInteractions(mWorkspaceItemSpaceFinder) } @Test @@ -191,22 +185,14 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { ): List<AddedItem> { setupWorkspaces(nonEmptyScreenIds) val task = newTask(*itemsToAdd) - var updateCount = 0 - mModelHelper.executeTaskForTest(task).forEach { - updateCount++ - it.run() - } val addedItems = mutableListOf<AddedItem>() - if (updateCount > 0) { - verify(mDataModelCallbacks) - .bindAppsAdded( - any(), - mNotAnimatedItemArgumentCaptor.capture(), - mAnimatedItemArgumentCaptor.capture() - ) - addedItems.addAll(mAnimatedItemArgumentCaptor.value.map { AddedItem(it, true) }) - addedItems.addAll(mNotAnimatedItemArgumentCaptor.value.map { AddedItem(it, false) }) + + runOnExecutorSync(Executors.MODEL_EXECUTOR) { + mDataModelCallbacks.addedItems.clear() + mModelHelper.model.enqueueModelUpdateTask(task) + runOnExecutorSync(Executors.MAIN_EXECUTOR) {} + addedItems.addAll(mDataModelCallbacks.addedItems) } return addedItems @@ -224,3 +210,17 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { } private data class AddedItem(val itemInfo: ItemInfo, val isAnimated: Boolean) + +private class MyCallbacks : BgDataModel.Callbacks { + + val addedItems = mutableListOf<AddedItem>() + + override fun bindAppsAdded( + newScreens: IntArray?, + addNotAnimated: ArrayList<ItemInfo>, + addAnimated: ArrayList<ItemInfo> + ) { + addedItems.addAll(addAnimated.map { AddedItem(it, true) }) + addedItems.addAll(addNotAnimated.map { AddedItem(it, false) }) + } +} diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java index f55b244c24..f7710522bb 100644 --- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java +++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java @@ -1,32 +1,31 @@ package com.android.launcher3.model; +import static android.os.Process.myUserHandle; + +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY; +import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2; +import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY3; +import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE; +import static com.android.launcher3.util.TestUtil.runOnExecutorSync; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; -import android.content.ComponentName; import android.content.Context; -import android.content.pm.PackageInfo; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.Color; -import android.os.Process; -import android.os.UserHandle; -import android.os.UserManager; - -import androidx.annotation.NonNull; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.launcher3.LauncherAppState; import com.android.launcher3.icons.BitmapInfo; -import com.android.launcher3.icons.IconCache; -import com.android.launcher3.icons.cache.CachingLogic; -import com.android.launcher3.model.data.AppInfo; -import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.util.IntSet; +import com.android.launcher3.util.LauncherLayoutBuilder; import com.android.launcher3.util.LauncherModelHelper; +import com.android.launcher3.util.PackageUserKey; import org.junit.After; import org.junit.Before; @@ -35,6 +34,7 @@ import org.junit.runner.RunWith; import java.util.Arrays; import java.util.HashSet; +import java.util.List; /** * Tests for {@link CacheDataUpdatedTask} @@ -43,49 +43,40 @@ import java.util.HashSet; @RunWith(AndroidJUnit4.class) public class CacheDataUpdatedTaskTest { - private static final String NEW_LABEL_PREFIX = "new-label-"; + private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1"; + private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2"; private LauncherModelHelper mModelHelper; + private Context mContext; + + private int mSession1; @Before public void setup() throws Exception { mModelHelper = new LauncherModelHelper(); - mModelHelper.initializeData("cache_data_updated_task_data"); - - // Add placeholder entries in the cache to simulate update - Context context = mModelHelper.sandboxContext; - IconCache iconCache = LauncherAppState.getInstance(context).getIconCache(); - CachingLogic<ItemInfo> placeholderLogic = new CachingLogic<ItemInfo>() { - @Override - @NonNull - public ComponentName getComponent(@NonNull ItemInfo info) { - return info.getTargetComponent(); - } - - @NonNull - @Override - public UserHandle getUser(@NonNull ItemInfo info) { - return info.user; - } - - @NonNull - @Override - public CharSequence getLabel(@NonNull ItemInfo info) { - return NEW_LABEL_PREFIX + info.id; - } - - @NonNull - @Override - public BitmapInfo loadIcon(@NonNull Context context, @NonNull ItemInfo info) { - return BitmapInfo.of(Bitmap.createBitmap(1, 1, Config.ARGB_8888), Color.RED); - } - }; - - UserManager um = context.getSystemService(UserManager.class); - for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) { - iconCache.addIconToDBAndMemCache(info, placeholderLogic, new PackageInfo(), - um.getSerialNumberForUser(info.user), true); - } + mContext = mModelHelper.sandboxContext; + mSession1 = mModelHelper.createInstallerSession(PENDING_APP_1); + mModelHelper.createInstallerSession(PENDING_APP_2); + + LauncherLayoutBuilder builder = new LauncherLayoutBuilder() + .atHotseat(1).putFolder("MyFolder") + .addApp(TEST_PACKAGE, TEST_ACTIVITY) // 2 + .addApp(TEST_PACKAGE, TEST_ACTIVITY2) // 3 + .addApp(TEST_PACKAGE, TEST_ACTIVITY3) // 4 + + // Pending App 1 + .addApp(PENDING_APP_1, TEST_ACTIVITY) // 5 + .addApp(PENDING_APP_1, TEST_ACTIVITY2) // 6 + .addApp(PENDING_APP_1, TEST_ACTIVITY3) // 7 + + // Pending App 2 + .addApp(PENDING_APP_2, TEST_ACTIVITY) // 8 + .addApp(PENDING_APP_2, TEST_ACTIVITY2) // 9 + .addApp(PENDING_APP_2, TEST_ACTIVITY3) // 10 + .build(); + mModelHelper.setupDefaultLayoutProvider(builder); + mModelHelper.loadModelSync(); + assertEquals(10, mModelHelper.getBgDataModel().itemsIdMap.size()); } @After @@ -94,61 +85,71 @@ public class CacheDataUpdatedTaskTest { } private CacheDataUpdatedTask newTask(int op, String... pkg) { - return new CacheDataUpdatedTask(op, Process.myUserHandle(), + return new CacheDataUpdatedTask(op, myUserHandle(), new HashSet<>(Arrays.asList(pkg))); } @Test - public void testCacheUpdate_update_apps() throws Exception { - // Clear all icons from apps list so that its easy to check what was updated - for (AppInfo info : mModelHelper.getAllAppsList().data) { - info.bitmap = BitmapInfo.LOW_RES_INFO; - } - - mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1")); - - // Verify that only the app icons of app1 (id 1 & 2) are updated. Custom shortcut (id 7) - // is not updated - verifyUpdate(1, 2); - - // Verify that only app1 var updated in allAppsList - assertFalse(mModelHelper.getAllAppsList().data.isEmpty()); - for (AppInfo info : mModelHelper.getAllAppsList().data) { - if (info.componentName.getPackageName().equals("app1")) { - assertFalse(info.bitmap.isNullOrLowRes()); - } else { - assertTrue(info.bitmap.isNullOrLowRes()); - } - } + public void testCacheUpdate_update_apps() { + // Run on model executor so that no other task runs in the middle. + runOnExecutorSync(MODEL_EXECUTOR, () -> { + // Clear all icons from apps list so that its easy to check what was updated + allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO); + + mModelHelper.getModel().enqueueModelUpdateTask( + newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, TEST_PACKAGE)); + + // Verify that only the app icons of TEST_PACKAGE (id 2, 3, 4) are updated. + verifyUpdate(2, 3, 4); + }); } @Test - public void testSessionUpdate_ignores_normal_apps() throws Exception { - mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1")); - - // app1 has no restored shortcuts. Verify that nothing was updated. - verifyUpdate(); + public void testSessionUpdate_ignores_normal_apps() { + // Run on model executor so that no other task runs in the middle. + runOnExecutorSync(MODEL_EXECUTOR, () -> { + // Clear all icons from apps list so that its easy to check what was updated + allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO); + + mModelHelper.getModel().enqueueModelUpdateTask( + newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, TEST_PACKAGE)); + + // TEST_PACKAGE has no restored shortcuts. Verify that nothing was updated. + verifyUpdate(); + }); } @Test - public void testSessionUpdate_updates_pending_apps() throws Exception { - mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3")); - - // app3 has only restored apps (id 5, 6) and shortcuts (id 9). Verify that only apps were - // were updated - verifyUpdate(5, 6); + public void testSessionUpdate_updates_pending_apps() { + // Run on model executor so that no other task runs in the middle. + runOnExecutorSync(MODEL_EXECUTOR, () -> { + LauncherAppState.getInstance(mContext).getIconCache().updateSessionCache( + new PackageUserKey(PENDING_APP_1, myUserHandle()), + mContext.getPackageManager().getPackageInstaller().getSessionInfo(mSession1)); + + // Clear all icons from apps list so that its easy to check what was updated + allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO); + + mModelHelper.getModel().enqueueModelUpdateTask( + newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, PENDING_APP_1)); + + // Only restored apps from PENDING_APP_1 (id 5, 6, 7) are updated + verifyUpdate(5, 6, 7); + }); } - private void verifyUpdate(Integer... idsUpdated) { - HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated)); - for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) { + private void verifyUpdate(int... idsUpdated) { + IntSet updates = IntSet.wrap(idsUpdated); + for (WorkspaceItemInfo info : allItems()) { if (updates.contains(info.id)) { - assertEquals(NEW_LABEL_PREFIX + info.id, info.title); - assertFalse(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes()); + assertFalse(info.bitmap.isNullOrLowRes()); } else { - assertNotSame(NEW_LABEL_PREFIX + info.id, info.title); - assertTrue(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes()); + assertTrue(info.bitmap.isNullOrLowRes()); } } } + + private List<WorkspaceItemInfo> allItems() { + return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).contents; + } } diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt index 06b7e68ac8..04735f20b1 100644 --- a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt +++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt @@ -24,7 +24,6 @@ import android.graphics.Point import android.os.Process import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.InvariantDeviceProfile import com.android.launcher3.LauncherPrefs import com.android.launcher3.LauncherPrefs.Companion.WORKSPACE_SIZE @@ -108,8 +107,8 @@ class GridSizeMigrationUtilTest { fun testMigration() { // Src Hotseat icons addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE) - addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE) - addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE) addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE) // Src grid icons // _ _ _ _ _ @@ -124,7 +123,7 @@ class GridSizeMigrationUtilTest { addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 3, testPackage9, 9, TMP_TABLE) // Dest hotseat icons - addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2) // Dest grid icons addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage10) @@ -219,8 +218,8 @@ class GridSizeMigrationUtilTest { // Hotseat items in grid A // 1 2 _ 3 4 addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE) - addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE) - addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE) addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE) // Workspace items in grid A // _ _ _ _ _ @@ -235,7 +234,7 @@ class GridSizeMigrationUtilTest { // Hotseat items in grid B // 2 _ _ _ - addItem(ITEM_TYPE_SHORTCUT, 0, CONTAINER_HOTSEAT, 0, 0, testPackage2) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 0, CONTAINER_HOTSEAT, 0, 0, testPackage2) // Workspace items in grid B // _ _ _ _ // _ _ _ 10 @@ -291,7 +290,7 @@ class GridSizeMigrationUtilTest { null ) ?: throw IllegalStateException() - var locMap = parseLocMap(context, c) + var locMap = parseLocMap(c) // Expected items in grid B // _ _ _ _ // 5 6 7 8 @@ -348,7 +347,7 @@ class GridSizeMigrationUtilTest { null ) ?: throw IllegalStateException() - locMap = parseLocMap(context, c) + locMap = parseLocMap(c) // Expected workspace items in grid A // _ _ _ _ _ // _ _ _ _ 5 @@ -410,7 +409,7 @@ class GridSizeMigrationUtilTest { null ) ?: throw IllegalStateException() - locMap = parseLocMap(context, c) + locMap = parseLocMap(c) // Expected workspace items in grid B // _ _ _ _ // 5 6 _ 8 @@ -436,7 +435,7 @@ class GridSizeMigrationUtilTest { c.close() } - private fun parseLocMap(context: Context, c: Cursor): Map<String?, Triple<Int, Int, Int>> { + private fun parseLocMap(c: Cursor): Map<String?, Triple<Int, Int, Int>> { // Check workspace items val intentIndex = c.getColumnIndex(INTENT) val screenIndex = c.getColumnIndex(SCREEN) @@ -465,7 +464,16 @@ class GridSizeMigrationUtilTest { 1, TMP_TABLE ), - addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE), + addItem( + ITEM_TYPE_DEEP_SHORTCUT, + 1, + CONTAINER_HOTSEAT, + 0, + 0, + testPackage2, + 2, + TMP_TABLE + ), addItem( ITEM_TYPE_APPLICATION, 2, @@ -476,7 +484,16 @@ class GridSizeMigrationUtilTest { 3, TMP_TABLE ), - addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE) + addItem( + ITEM_TYPE_DEEP_SHORTCUT, + 3, + CONTAINER_HOTSEAT, + 0, + 0, + testPackage4, + 4, + TMP_TABLE + ) ) val numSrcDatabaseHotseatIcons = srcHotseatItems.size idp.numDatabaseHotseatIcons = 6 @@ -532,9 +549,9 @@ class GridSizeMigrationUtilTest { @Test fun migrateFromLargerHotseat() { addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE) - addItem(ITEM_TYPE_SHORTCUT, 2, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 2, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE) addItem(ITEM_TYPE_APPLICATION, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE) - addItem(ITEM_TYPE_SHORTCUT, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE) addItem(ITEM_TYPE_APPLICATION, 5, CONTAINER_HOTSEAT, 0, 0, testPackage5, 5, TMP_TABLE) idp.numDatabaseHotseatIcons = 4 diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java index 78812c0778..544ed6b01d 100644 --- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java +++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java @@ -30,7 +30,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.ICON; import static com.android.launcher3.LauncherSettings.Favorites.INTENT; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; -import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; import static com.android.launcher3.LauncherSettings.Favorites.OPTIONS; import static com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID; import static com.android.launcher3.LauncherSettings.Favorites.RANK; @@ -158,13 +158,13 @@ public class LoaderCursorTest { @Test public void loadSimpleShortcut() { - initCursor(ITEM_TYPE_SHORTCUT, "my-shortcut"); + initCursor(ITEM_TYPE_DEEP_SHORTCUT, "my-shortcut"); assertTrue(mLoaderCursor.moveToNext()); WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem(); assertTrue(mApp.getIconCache().isDefaultIcon(info.bitmap, info.user)); assertEquals("my-shortcut", info.title); - assertEquals(ITEM_TYPE_SHORTCUT, info.itemType); + assertEquals(ITEM_TYPE_DEEP_SHORTCUT, info.itemType); } @Test diff --git a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java index 519191e251..4ba61ac315 100644 --- a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java +++ b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java @@ -1,5 +1,12 @@ package com.android.launcher3.model; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY; +import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2; +import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY3; +import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE; +import static com.android.launcher3.util.TestUtil.runOnExecutorSync; + import static org.junit.Assert.assertEquals; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -9,6 +16,8 @@ import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pm.PackageInstallInfo; +import com.android.launcher3.util.IntSet; +import com.android.launcher3.util.LauncherLayoutBuilder; import com.android.launcher3.util.LauncherModelHelper; import org.junit.After; @@ -16,9 +25,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.Arrays; -import java.util.HashSet; - /** * Tests for {@link PackageInstallStateChangedTask} */ @@ -26,12 +32,36 @@ import java.util.HashSet; @RunWith(AndroidJUnit4.class) public class PackageInstallStateChangedTaskTest { + private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1"; + private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2"; + private LauncherModelHelper mModelHelper; + private IntSet mDownloadingApps; @Before public void setup() throws Exception { mModelHelper = new LauncherModelHelper(); - mModelHelper.initializeData("package_install_state_change_task_data"); + mModelHelper.createInstallerSession(PENDING_APP_1); + mModelHelper.createInstallerSession(PENDING_APP_2); + + LauncherLayoutBuilder builder = new LauncherLayoutBuilder() + .atWorkspace(0, 0, 1).putApp(TEST_PACKAGE, TEST_ACTIVITY) // 1 + .atWorkspace(0, 0, 2).putApp(TEST_PACKAGE, TEST_ACTIVITY2) // 2 + .atWorkspace(0, 0, 3).putApp(TEST_PACKAGE, TEST_ACTIVITY3) // 3 + + .atWorkspace(0, 0, 4).putApp(PENDING_APP_1, TEST_ACTIVITY) // 4 + .atWorkspace(0, 0, 5).putApp(PENDING_APP_1, TEST_ACTIVITY2) // 5 + .atWorkspace(0, 0, 6).putApp(PENDING_APP_1, TEST_ACTIVITY3) // 6 + .atWorkspace(0, 0, 7).putWidget(PENDING_APP_1, "pending.widget", 1, 1) // 7 + + .atWorkspace(0, 0, 8).putApp(PENDING_APP_2, TEST_ACTIVITY) // 8 + .atWorkspace(0, 0, 9).putApp(PENDING_APP_2, TEST_ACTIVITY2) // 9 + .atWorkspace(0, 0, 10).putApp(PENDING_APP_2, TEST_ACTIVITY3); // 10 + + mDownloadingApps = IntSet.wrap(4, 5, 6, 7, 8, 9, 10); + mModelHelper.setupDefaultLayoutProvider(builder); + mModelHelper.loadModelSync(); + assertEquals(10, mModelHelper.getBgDataModel().itemsIdMap.size()); } @After @@ -47,36 +77,45 @@ public class PackageInstallStateChangedTaskTest { } @Test - public void testSessionUpdate_ignore_installed() throws Exception { - mModelHelper.executeTaskForTest(newTask("app1", 30)); - - // No shortcuts were updated - verifyProgressUpdate(0); + public void testSessionUpdate_ignore_installed() { + // Run on model executor so that no other task runs in the middle. + runOnExecutorSync(MODEL_EXECUTOR, () -> { + mModelHelper.getModel().enqueueModelUpdateTask(newTask(TEST_PACKAGE, 30)); + + // No shortcuts were updated + verifyProgressUpdate(0); + }); } @Test - public void testSessionUpdate_shortcuts_updated() throws Exception { - mModelHelper.executeTaskForTest(newTask("app3", 30)); + public void testSessionUpdate_shortcuts_updated() { + // Run on model executor so that no other task runs in the middle. + runOnExecutorSync(MODEL_EXECUTOR, () -> { + mModelHelper.getModel().enqueueModelUpdateTask(newTask(PENDING_APP_1, 30)); - verifyProgressUpdate(30, 5, 6, 7); + verifyProgressUpdate(30, 4, 5, 6, 7); + }); } @Test - public void testSessionUpdate_widgets_updated() throws Exception { - mModelHelper.executeTaskForTest(newTask("app4", 30)); + public void testSessionUpdate_widgets_updated() { + // Run on model executor so that no other task runs in the middle. + runOnExecutorSync(MODEL_EXECUTOR, () -> { + mModelHelper.getModel().enqueueModelUpdateTask(newTask(PENDING_APP_2, 30)); - verifyProgressUpdate(30, 8, 9); + verifyProgressUpdate(30, 8, 9, 10); + }); } - private void verifyProgressUpdate(int progress, Integer... idsUpdated) { - HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated)); + private void verifyProgressUpdate(int progress, int... idsUpdated) { + IntSet updates = IntSet.wrap(idsUpdated); for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) { - if (info instanceof WorkspaceItemInfo) { - assertEquals(updates.contains(info.id) ? progress: 100, - ((WorkspaceItemInfo) info).getProgressLevel()); + int expectedProgress = updates.contains(info.id) ? progress + : (mDownloadingApps.contains(info.id) ? 0 : 100); + if (info instanceof WorkspaceItemInfo wi) { + assertEquals(expectedProgress, wi.getProgressLevel()); } else { - assertEquals(updates.contains(info.id) ? progress: -1, - ((LauncherAppWidgetInfo) info).installProgress); + assertEquals(expectedProgress, ((LauncherAppWidgetInfo) info).installProgress); } } } diff --git a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt index a81413e1d3..270672f209 100644 --- a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt +++ b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt @@ -64,8 +64,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tinv.numColumns: 5\n" + "\tinv.numSearchContainerColumns: 5\n" + "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 165.0px (62.857143dp)\n" + - "\tcellHeightPx: 235.0px (89.52381dp)\n" + + "\tcellWidthPx: 159.0px (60.57143dp)\n" + + "\tcellHeightPx: 229.0px (87.2381dp)\n" + "\tgetCellSize().x: 207.0px (78.85714dp)\n" + "\tgetCellSize().y: 379.0px (144.38095dp)\n" + "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" + @@ -76,15 +76,16 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tcellLayoutPaddingPx.bottom: 28.0px (10.666667dp)\n" + "\ticonSizePx: 147.0px (56.0dp)\n" + "\ticonTextSizePx: 38.0px (14.476191dp)\n" + - "\ticonDrawablePaddingPx: 18.0px (6.857143dp)\n" + + "\ticonDrawablePaddingPx: 12.0px (4.571429dp)\n" + "\tinv.numFolderRows: 4\n" + "\tinv.numFolderColumns: 4\n" + "\tfolderCellWidthPx: 195.0px (74.28571dp)\n" + "\tfolderCellHeightPx: 230.0px (87.61905dp)\n" + "\tfolderChildIconSizePx: 147.0px (56.0dp)\n" + "\tfolderChildTextSizePx: 38.0px (14.476191dp)\n" + - "\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" + - "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" + + "\tfolderChildDrawablePaddingPx: 4.0px (1.5238096dp)\n" + + "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + + "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + "\tfolderTopPadding: 63.0px (24.0dp)\n" + "\tfolderFooterHeight: 147.0px (56.0dp)\n" + @@ -105,7 +106,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" + "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" + "\tnumShownAllAppsColumns: 5\n" + - "\tallAppsLeftRightPadding: 21.0px (8.0dp)\n" + + "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" + "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" + "\thotseatBarSizePx: 294.0px (112.0dp)\n" + "\tinv.hotseatColumnSpan: 5\n" + @@ -138,8 +139,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tworkspacePadding.bottom: 203.0px (77.333336dp)\n" + "\ticonScale: 1.0px (0.3809524dp)\n" + "\tcellScaleToFit : 1.0px (0.3809524dp)\n" + - "\textraSpace: 722.0px (275.0476dp)\n" + - "\tunscaled extraSpace: 722.0px (275.0476dp)\n" + + "\textraSpace: 752.0px (286.4762dp)\n" + + "\tunscaled extraSpace: 752.0px (286.4762dp)\n" + "\tmaxEmptySpace: 0.0px (0.0dp)\n" + "\tworkspaceTopPadding: 0.0px (0.0dp)\n" + "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" + @@ -200,8 +201,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tinv.numColumns: 5\n" + "\tinv.numSearchContainerColumns: 5\n" + "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 165.0px (62.857143dp)\n" + - "\tcellHeightPx: 235.0px (89.52381dp)\n" + + "\tcellWidthPx: 159.0px (60.57143dp)\n" + + "\tcellHeightPx: 229.0px (87.2381dp)\n" + "\tgetCellSize().x: 207.0px (78.85714dp)\n" + "\tgetCellSize().y: 383.0px (145.90475dp)\n" + "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" + @@ -212,15 +213,16 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tcellLayoutPaddingPx.bottom: 28.0px (10.666667dp)\n" + "\ticonSizePx: 147.0px (56.0dp)\n" + "\ticonTextSizePx: 38.0px (14.476191dp)\n" + - "\ticonDrawablePaddingPx: 18.0px (6.857143dp)\n" + + "\ticonDrawablePaddingPx: 12.0px (4.571429dp)\n" + "\tinv.numFolderRows: 4\n" + "\tinv.numFolderColumns: 4\n" + "\tfolderCellWidthPx: 195.0px (74.28571dp)\n" + "\tfolderCellHeightPx: 230.0px (87.61905dp)\n" + "\tfolderChildIconSizePx: 147.0px (56.0dp)\n" + "\tfolderChildTextSizePx: 38.0px (14.476191dp)\n" + - "\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" + - "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" + + "\tfolderChildDrawablePaddingPx: 4.0px (1.5238096dp)\n" + + "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + + "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + "\tfolderTopPadding: 63.0px (24.0dp)\n" + "\tfolderFooterHeight: 147.0px (56.0dp)\n" + @@ -241,7 +243,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" + "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" + "\tnumShownAllAppsColumns: 5\n" + - "\tallAppsLeftRightPadding: 21.0px (8.0dp)\n" + + "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" + "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" + "\thotseatBarSizePx: 273.0px (104.0dp)\n" + "\tinv.hotseatColumnSpan: 5\n" + @@ -274,8 +276,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tworkspacePadding.bottom: 245.0px (93.333336dp)\n" + "\ticonScale: 1.0px (0.3809524dp)\n" + "\tcellScaleToFit : 1.0px (0.3809524dp)\n" + - "\textraSpace: 743.0px (283.0476dp)\n" + - "\tunscaled extraSpace: 743.0px (283.0476dp)\n" + + "\textraSpace: 773.0px (294.4762dp)\n" + + "\tunscaled extraSpace: 773.0px (294.4762dp)\n" + "\tmaxEmptySpace: 0.0px (0.0dp)\n" + "\tworkspaceTopPadding: 0.0px (0.0dp)\n" + "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" + @@ -336,7 +338,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tinv.numColumns: 5\n" + "\tinv.numSearchContainerColumns: 5\n" + "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 158.0px (60.190475dp)\n" + + "\tcellWidthPx: 152.0px (57.904762dp)\n" + "\tcellHeightPx: 166.0px (63.238094dp)\n" + "\tgetCellSize().x: 368.0px (140.19048dp)\n" + "\tgetCellSize().y: 193.0px (73.52381dp)\n" + @@ -355,8 +357,9 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tfolderCellHeightPx: 205.0px (78.09524dp)\n" + "\tfolderChildIconSizePx: 131.0px (49.904762dp)\n" + "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" + - "\tfolderChildDrawablePaddingPx: 9.0px (3.4285715dp)\n" + - "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" + + "\tfolderChildDrawablePaddingPx: 4.0px (1.5238096dp)\n" + + "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + + "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + "\tfolderTopPadding: 56.0px (21.333334dp)\n" + "\tfolderFooterHeight: 131.0px (49.904762dp)\n" + @@ -472,7 +475,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tinv.numColumns: 5\n" + "\tinv.numSearchContainerColumns: 5\n" + "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 158.0px (60.190475dp)\n" + + "\tcellWidthPx: 152.0px (57.904762dp)\n" + "\tcellHeightPx: 166.0px (63.238094dp)\n" + "\tgetCellSize().x: 393.0px (149.71428dp)\n" + "\tgetCellSize().y: 180.0px (68.57143dp)\n" + @@ -491,8 +494,9 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tfolderCellHeightPx: 192.0px (73.14286dp)\n" + "\tfolderChildIconSizePx: 123.0px (46.857143dp)\n" + "\tfolderChildTextSizePx: 32.0px (12.190476dp)\n" + - "\tfolderChildDrawablePaddingPx: 8.0px (3.047619dp)\n" + - "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" + + "\tfolderChildDrawablePaddingPx: 3.0px (1.1428572dp)\n" + + "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + + "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + "\tfolderTopPadding: 53.0px (20.190475dp)\n" + "\tfolderFooterHeight: 123.0px (46.857143dp)\n" + @@ -621,15 +625,16 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tcellLayoutPaddingPx.bottom: 59.0px (29.5dp)\n" + "\ticonSizePx: 120.0px (60.0dp)\n" + "\ticonTextSizePx: 28.0px (14.0dp)\n" + - "\ticonDrawablePaddingPx: 14.0px (7.0dp)\n" + + "\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" + "\tinv.numFolderRows: 3\n" + "\tinv.numFolderColumns: 3\n" + "\tfolderCellWidthPx: 240.0px (120.0dp)\n" + "\tfolderCellHeightPx: 208.0px (104.0dp)\n" + "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" + "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" + - "\tfolderChildDrawablePaddingPx: 16.0px (8.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" + + "\tfolderChildDrawablePaddingPx: 11.0px (5.5dp)\n" + + "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + + "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" + "\tfolderTopPadding: 48.0px (24.0dp)\n" + "\tfolderFooterHeight: 112.0px (56.0dp)\n" + @@ -644,7 +649,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tallAppsCloseDuration: 500\n" + "\tallAppsIconSizePx: 120.0px (60.0dp)\n" + "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" + - "\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\n" + + "\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" + "\tallAppsCellHeightPx: 284.0px (142.0dp)\n" + "\tallAppsCellWidthPx: 252.0px (126.0dp)\n" + "\tallAppsBorderSpacePxX: 32.0px (16.0dp)\n" + @@ -758,15 +763,16 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tcellLayoutPaddingPx.bottom: 59.0px (29.5dp)\n" + "\ticonSizePx: 120.0px (60.0dp)\n" + "\ticonTextSizePx: 28.0px (14.0dp)\n" + - "\ticonDrawablePaddingPx: 14.0px (7.0dp)\n" + + "\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" + "\tinv.numFolderRows: 3\n" + "\tinv.numFolderColumns: 3\n" + "\tfolderCellWidthPx: 240.0px (120.0dp)\n" + "\tfolderCellHeightPx: 208.0px (104.0dp)\n" + "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" + "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" + - "\tfolderChildDrawablePaddingPx: 16.0px (8.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" + + "\tfolderChildDrawablePaddingPx: 11.0px (5.5dp)\n" + + "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + + "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" + "\tfolderTopPadding: 48.0px (24.0dp)\n" + "\tfolderFooterHeight: 112.0px (56.0dp)\n" + @@ -781,7 +787,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tallAppsCloseDuration: 500\n" + "\tallAppsIconSizePx: 120.0px (60.0dp)\n" + "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" + - "\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\n" + + "\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" + "\tallAppsCellHeightPx: 284.0px (142.0dp)\n" + "\tallAppsCellWidthPx: 252.0px (126.0dp)\n" + "\tallAppsBorderSpacePxX: 32.0px (16.0dp)\n" + @@ -895,15 +901,16 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tcellLayoutPaddingPx.bottom: 72.0px (36.0dp)\n" + "\ticonSizePx: 120.0px (60.0dp)\n" + "\ticonTextSizePx: 28.0px (14.0dp)\n" + - "\ticonDrawablePaddingPx: 14.0px (7.0dp)\n" + + "\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" + "\tinv.numFolderRows: 3\n" + "\tinv.numFolderColumns: 3\n" + "\tfolderCellWidthPx: 204.0px (102.0dp)\n" + "\tfolderCellHeightPx: 240.0px (120.0dp)\n" + "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" + "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" + - "\tfolderChildDrawablePaddingPx: 27.0px (13.5dp)\n" + - "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" + + "\tfolderChildDrawablePaddingPx: 22.0px (11.0dp)\n" + + "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + + "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" + "\tfolderTopPadding: 48.0px (24.0dp)\n" + "\tfolderFooterHeight: 112.0px (56.0dp)\n" + @@ -918,7 +925,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tallAppsCloseDuration: 500\n" + "\tallAppsIconSizePx: 120.0px (60.0dp)\n" + "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" + - "\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\n" + + "\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" + "\tallAppsCellHeightPx: 316.0px (158.0dp)\n" + "\tallAppsCellWidthPx: 192.0px (96.0dp)\n" + "\tallAppsBorderSpacePxX: 16.0px (8.0dp)\n" + @@ -1032,15 +1039,16 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tcellLayoutPaddingPx.bottom: 72.0px (36.0dp)\n" + "\ticonSizePx: 120.0px (60.0dp)\n" + "\ticonTextSizePx: 28.0px (14.0dp)\n" + - "\ticonDrawablePaddingPx: 14.0px (7.0dp)\n" + + "\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" + "\tinv.numFolderRows: 3\n" + "\tinv.numFolderColumns: 3\n" + "\tfolderCellWidthPx: 204.0px (102.0dp)\n" + "\tfolderCellHeightPx: 240.0px (120.0dp)\n" + "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" + "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" + - "\tfolderChildDrawablePaddingPx: 27.0px (13.5dp)\n" + - "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" + + "\tfolderChildDrawablePaddingPx: 22.0px (11.0dp)\n" + + "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + + "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" + "\tfolderTopPadding: 48.0px (24.0dp)\n" + "\tfolderFooterHeight: 112.0px (56.0dp)\n" + @@ -1055,7 +1063,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tallAppsCloseDuration: 500\n" + "\tallAppsIconSizePx: 120.0px (60.0dp)\n" + "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" + - "\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\n" + + "\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" + "\tallAppsCellHeightPx: 316.0px (158.0dp)\n" + "\tallAppsCellWidthPx: 192.0px (96.0dp)\n" + "\tallAppsBorderSpacePxX: 16.0px (8.0dp)\n" + @@ -1162,8 +1170,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tinv.numColumns: 4\n" + "\tinv.numSearchContainerColumns: 4\n" + "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 159.0px (60.57143dp)\n" + - "\tcellHeightPx: 223.0px (84.95238dp)\n" + + "\tcellWidthPx: 154.0px (58.666668dp)\n" + + "\tcellHeightPx: 218.0px (83.04762dp)\n" + "\tgetCellSize().x: 270.0px (102.85714dp)\n" + "\tgetCellSize().y: 342.0px (130.28572dp)\n" + "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" + @@ -1174,15 +1182,16 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" + "\ticonSizePx: 141.0px (53.714287dp)\n" + "\ticonTextSizePx: 34.0px (12.952381dp)\n" + - "\ticonDrawablePaddingPx: 18.0px (6.857143dp)\n" + + "\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" + "\tinv.numFolderRows: 3\n" + "\tinv.numFolderColumns: 4\n" + "\tfolderCellWidthPx: 189.0px (72.0dp)\n" + "\tfolderCellHeightPx: 219.0px (83.42857dp)\n" + "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" + "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" + - "\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" + - "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" + + "\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" + + "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + + "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + "\tfolderTopPadding: 63.0px (24.0dp)\n" + "\tfolderFooterHeight: 147.0px (56.0dp)\n" + @@ -1236,8 +1245,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" + "\ticonScale: 1.0px (0.3809524dp)\n" + "\tcellScaleToFit : 1.0px (0.3809524dp)\n" + - "\textraSpace: 478.0px (182.09525dp)\n" + - "\tunscaled extraSpace: 478.0px (182.09525dp)\n" + + "\textraSpace: 498.0px (189.71428dp)\n" + + "\tunscaled extraSpace: 498.0px (189.71428dp)\n" + "\tmaxEmptySpace: 0.0px (0.0dp)\n" + "\tworkspaceTopPadding: 0.0px (0.0dp)\n" + "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" + @@ -1303,8 +1312,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tinv.numColumns: 4\n" + "\tinv.numSearchContainerColumns: 4\n" + "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 159.0px (60.57143dp)\n" + - "\tcellHeightPx: 223.0px (84.95238dp)\n" + + "\tcellWidthPx: 154.0px (58.666668dp)\n" + + "\tcellHeightPx: 218.0px (83.04762dp)\n" + "\tgetCellSize().x: 270.0px (102.85714dp)\n" + "\tgetCellSize().y: 342.0px (130.28572dp)\n" + "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" + @@ -1315,15 +1324,16 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" + "\ticonSizePx: 141.0px (53.714287dp)\n" + "\ticonTextSizePx: 34.0px (12.952381dp)\n" + - "\ticonDrawablePaddingPx: 18.0px (6.857143dp)\n" + + "\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" + "\tinv.numFolderRows: 3\n" + "\tinv.numFolderColumns: 4\n" + "\tfolderCellWidthPx: 189.0px (72.0dp)\n" + "\tfolderCellHeightPx: 219.0px (83.42857dp)\n" + "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" + "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" + - "\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" + - "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" + + "\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" + + "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + + "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + "\tfolderTopPadding: 63.0px (24.0dp)\n" + "\tfolderFooterHeight: 147.0px (56.0dp)\n" + @@ -1377,8 +1387,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" + "\ticonScale: 1.0px (0.3809524dp)\n" + "\tcellScaleToFit : 1.0px (0.3809524dp)\n" + - "\textraSpace: 478.0px (182.09525dp)\n" + - "\tunscaled extraSpace: 478.0px (182.09525dp)\n" + + "\textraSpace: 498.0px (189.71428dp)\n" + + "\tunscaled extraSpace: 498.0px (189.71428dp)\n" + "\tmaxEmptySpace: 0.0px (0.0dp)\n" + "\tworkspaceTopPadding: 0.0px (0.0dp)\n" + "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" + @@ -1444,8 +1454,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tinv.numColumns: 4\n" + "\tinv.numSearchContainerColumns: 4\n" + "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 159.0px (60.57143dp)\n" + - "\tcellHeightPx: 223.0px (84.95238dp)\n" + + "\tcellWidthPx: 154.0px (58.666668dp)\n" + + "\tcellHeightPx: 218.0px (83.04762dp)\n" + "\tgetCellSize().x: 224.0px (85.333336dp)\n" + "\tgetCellSize().y: 430.0px (163.80952dp)\n" + "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" + @@ -1456,15 +1466,16 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" + "\ticonSizePx: 141.0px (53.714287dp)\n" + "\ticonTextSizePx: 34.0px (12.952381dp)\n" + - "\ticonDrawablePaddingPx: 18.0px (6.857143dp)\n" + + "\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" + "\tinv.numFolderRows: 3\n" + "\tinv.numFolderColumns: 4\n" + "\tfolderCellWidthPx: 189.0px (72.0dp)\n" + "\tfolderCellHeightPx: 219.0px (83.42857dp)\n" + "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" + "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" + - "\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" + - "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" + + "\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" + + "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + + "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + "\tfolderTopPadding: 63.0px (24.0dp)\n" + "\tfolderFooterHeight: 147.0px (56.0dp)\n" + @@ -1518,8 +1529,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" + "\ticonScale: 1.0px (0.3809524dp)\n" + "\tcellScaleToFit : 1.0px (0.3809524dp)\n" + - "\textraSpace: 829.0px (315.8095dp)\n" + - "\tunscaled extraSpace: 829.0px (315.8095dp)\n" + + "\textraSpace: 849.0px (323.42856dp)\n" + + "\tunscaled extraSpace: 849.0px (323.42856dp)\n" + "\tmaxEmptySpace: 0.0px (0.0dp)\n" + "\tworkspaceTopPadding: 0.0px (0.0dp)\n" + "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" + @@ -1581,8 +1592,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tinv.numColumns: 4\n" + "\tinv.numSearchContainerColumns: 4\n" + "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 159.0px (60.57143dp)\n" + - "\tcellHeightPx: 223.0px (84.95238dp)\n" + + "\tcellWidthPx: 154.0px (58.666668dp)\n" + + "\tcellHeightPx: 218.0px (83.04762dp)\n" + "\tgetCellSize().x: 224.0px (85.333336dp)\n" + "\tgetCellSize().y: 430.0px (163.80952dp)\n" + "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" + @@ -1593,15 +1604,16 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" + "\ticonSizePx: 141.0px (53.714287dp)\n" + "\ticonTextSizePx: 34.0px (12.952381dp)\n" + - "\ticonDrawablePaddingPx: 18.0px (6.857143dp)\n" + + "\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" + "\tinv.numFolderRows: 3\n" + "\tinv.numFolderColumns: 4\n" + "\tfolderCellWidthPx: 189.0px (72.0dp)\n" + "\tfolderCellHeightPx: 219.0px (83.42857dp)\n" + "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" + "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" + - "\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" + - "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" + + "\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" + + "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + + "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + "\tfolderTopPadding: 63.0px (24.0dp)\n" + "\tfolderFooterHeight: 147.0px (56.0dp)\n" + @@ -1655,8 +1667,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" + "\ticonScale: 1.0px (0.3809524dp)\n" + "\tcellScaleToFit : 1.0px (0.3809524dp)\n" + - "\textraSpace: 829.0px (315.8095dp)\n" + - "\tunscaled extraSpace: 829.0px (315.8095dp)\n" + + "\textraSpace: 849.0px (323.42856dp)\n" + + "\tunscaled extraSpace: 849.0px (323.42856dp)\n" + "\tmaxEmptySpace: 0.0px (0.0dp)\n" + "\tworkspaceTopPadding: 0.0px (0.0dp)\n" + "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" + diff --git a/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt b/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt index 2a27487e3b..d10239762b 100644 --- a/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt +++ b/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt @@ -158,4 +158,25 @@ class HotseatWidthCalculationTest : FakeInvariantDeviceProfileTest() { assertThat(dp.isQsbInline).isFalse() assertThat(dp.hotseatQsbWidth).isEqualTo(1095) } + + @Test + fun border_space_should_be_zero_when_numHotseatIcons_is_smallerOrEqual_1() { + initializeVarsForTablet(isGestureMode = false) + windowBounds = WindowBounds(Rect(0, 0, 1800, 2560), Rect(0, 104, 0, 0)) + + val numShownHotseatIcons = listOf(-1, 0, 1) + for (numHotseatIcons in numShownHotseatIcons) { + inv?.numShownHotseatIcons = numHotseatIcons + + val dp = newDP() + dp.isTaskbarPresentInApps = true + + assertThat(dp.numShownHotseatIcons).isEqualTo(numHotseatIcons) + assertThat(dp.hotseatBorderSpace).isEqualTo(0) + + assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(177) + assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(177) + assertThat(dp.hotseatQsbWidth).isEqualTo(1445) + } + } } diff --git a/tests/src/com/android/launcher3/responsive/AllAppsSpecsTest.kt b/tests/src/com/android/launcher3/responsive/AllAppsSpecsTest.kt new file mode 100644 index 0000000000..cd95e99b99 --- /dev/null +++ b/tests/src/com/android/launcher3/responsive/AllAppsSpecsTest.kt @@ -0,0 +1,118 @@ +/* + * 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.launcher3.responsive + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.tests.R as TestR +import com.android.launcher3.util.TestResourceHelper +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class AllAppsSpecsTest : AbstractDeviceProfileTest() { + override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + + @Before + fun setup() { + initializeVarsForPhone(deviceSpecs["phone"]!!) + } + + @Test + fun parseValidFile() { + val allAppsSpecs = + AllAppsSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_all_apps_file)) + assertThat(allAppsSpecs.heightSpecs.size).isEqualTo(1) + assertThat(allAppsSpecs.heightSpecs[0].toString()) + .isEqualTo( + "AllAppsSpec(" + + "maxAvailableSize=26247, " + + "specType=HEIGHT, " + + "startPadding=SizeSpec(fixedSize=0.0, " + + "ofAvailableSpace=0.0, " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + + "endPadding=SizeSpec(fixedSize=0.0, " + + "ofAvailableSpace=0.0, " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + + "gutter=SizeSpec(fixedSize=0.0, " + + "ofAvailableSpace=0.0, " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=true, " + + "maxSize=2147483647), " + + "cellSize=SizeSpec(fixedSize=0.0, " + + "ofAvailableSpace=0.0, " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=true, " + + "maxSize=2147483647)" + + ")" + ) + + assertThat(allAppsSpecs.widthSpecs.size).isEqualTo(1) + assertThat(allAppsSpecs.widthSpecs[0].toString()) + .isEqualTo( + "AllAppsSpec(" + + "maxAvailableSize=26247, " + + "specType=WIDTH, " + + "startPadding=SizeSpec(fixedSize=0.0, " + + "ofAvailableSpace=0.0, " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=true, " + + "maxSize=2147483647), " + + "endPadding=SizeSpec(fixedSize=0.0, " + + "ofAvailableSpace=0.0, " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=true, " + + "maxSize=2147483647), " + + "gutter=SizeSpec(fixedSize=0.0, " + + "ofAvailableSpace=0.0, " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=true, " + + "maxSize=2147483647), " + + "cellSize=SizeSpec(fixedSize=0.0, " + + "ofAvailableSpace=0.0, " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=true, " + + "maxSize=2147483647)" + + ")" + ) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_missingTag_throwsError() { + AllAppsSpecs.create(TestResourceHelper(context!!, TestR.xml.invalid_all_apps_file_case_1)) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_moreThanOneValuePerTag_throwsError() { + AllAppsSpecs.create(TestResourceHelper(context!!, TestR.xml.invalid_all_apps_file_case_2)) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_valueBiggerThan1_throwsError() { + AllAppsSpecs.create(TestResourceHelper(context!!, TestR.xml.invalid_all_apps_file_case_3)) + } +} diff --git a/tests/src/com/android/launcher3/responsive/CalculatedAllAppsSpecTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedAllAppsSpecTest.kt new file mode 100644 index 0000000000..0f12e58a33 --- /dev/null +++ b/tests/src/com/android/launcher3/responsive/CalculatedAllAppsSpecTest.kt @@ -0,0 +1,76 @@ +/* + * 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.launcher3.responsive + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.tests.R as TestR +import com.android.launcher3.util.TestResourceHelper +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class CalculatedAllAppsSpecTest : AbstractDeviceProfileTest() { + override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + + /** + * This test tests: + * - (height spec) copy values from workspace + * - (width spec) copy values from workspace + */ + @Test + fun normalPhone_copiesFromWorkspace() { + val deviceSpec = deviceSpecs["phone"]!! + initializeVarsForPhone(deviceSpec) + + val availableWidth = deviceSpec.naturalSize.first + // Hotseat size is roughly 495px on a real device, + // it doesn't need to be precise on unit tests + val availableHeight = deviceSpec.naturalSize.second - deviceSpec.statusBarNaturalPx - 495 + + val workspaceSpecs = + WorkspaceSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_workspace_file)) + val widthSpec = workspaceSpecs.getCalculatedWidthSpec(4, availableWidth) + val heightSpec = workspaceSpecs.getCalculatedHeightSpec(5, availableHeight) + + val allAppsSpecs = + AllAppsSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_all_apps_file)) + + with(allAppsSpecs.getCalculatedWidthSpec(4, availableWidth, widthSpec)) { + assertThat(availableSpace).isEqualTo(availableWidth) + assertThat(cells).isEqualTo(4) + assertThat(startPaddingPx).isEqualTo(widthSpec.startPaddingPx) + assertThat(endPaddingPx).isEqualTo(widthSpec.endPaddingPx) + assertThat(gutterPx).isEqualTo(widthSpec.gutterPx) + assertThat(cellSizePx).isEqualTo(widthSpec.cellSizePx) + } + + with(allAppsSpecs.getCalculatedHeightSpec(5, availableHeight, heightSpec)) { + assertThat(availableSpace).isEqualTo(availableHeight) + assertThat(cells).isEqualTo(5) + assertThat(startPaddingPx).isEqualTo(0) + assertThat(endPaddingPx).isEqualTo(0) + assertThat(gutterPx).isEqualTo(heightSpec.gutterPx) + assertThat(cellSizePx).isEqualTo(heightSpec.cellSizePx) + } + } +} diff --git a/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt new file mode 100644 index 0000000000..f2a269a607 --- /dev/null +++ b/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt @@ -0,0 +1,120 @@ +/* + * 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.launcher3.responsive + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.tests.R +import com.android.launcher3.util.TestResourceHelper +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class CalculatedFolderSpecsTest : AbstractDeviceProfileTest() { + override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + + private val deviceSpec = deviceSpecs["phone"]!! + + @Before + fun setup() { + initializeVarsForPhone(deviceSpec) + } + + @Test + fun validate_matchWidthWorkspace() { + val columns = 6 + + // Loading workspace specs + val resourceHelperWorkspace = TestResourceHelper(context!!, R.xml.valid_workspace_file) + val workspaceSpecs = WorkspaceSpecs.create(resourceHelperWorkspace) + + // Loading folders specs + val resourceHelperFolder = TestResourceHelper(context!!, R.xml.valid_folders_specs) + val folderSpecs = FolderSpecs.create(resourceHelperFolder) + + assertThat(folderSpecs.widthSpecs.size).isEqualTo(2) + assertThat(folderSpecs.widthSpecs[0].cellSize.matchWorkspace).isEqualTo(true) + assertThat(folderSpecs.widthSpecs[1].cellSize.matchWorkspace).isEqualTo(false) + + // Validate width spec <= 800 + var availableWidth = deviceSpec.naturalSize.first + var calculatedWorkspace = workspaceSpecs.getCalculatedWidthSpec(columns, availableWidth) + var calculatedWidthFolderSpec = + folderSpecs.getCalculatedWidthSpec(columns, availableWidth, calculatedWorkspace) + with(calculatedWidthFolderSpec) { + assertThat(availableSpace).isEqualTo(availableWidth) + assertThat(cells).isEqualTo(columns) + assertThat(startPaddingPx).isEqualTo(16.dpToPx()) + assertThat(endPaddingPx).isEqualTo(16.dpToPx()) + assertThat(gutterPx).isEqualTo(16.dpToPx()) + assertThat(cellSizePx).isEqualTo(calculatedWorkspace.cellSizePx) + } + + // Validate width spec > 800 + availableWidth = 2000.dpToPx() + calculatedWorkspace = workspaceSpecs.getCalculatedWidthSpec(columns, availableWidth) + calculatedWidthFolderSpec = + folderSpecs.getCalculatedWidthSpec(columns, availableWidth, calculatedWorkspace) + with(calculatedWidthFolderSpec) { + assertThat(availableSpace).isEqualTo(availableWidth) + assertThat(cells).isEqualTo(columns) + assertThat(startPaddingPx).isEqualTo(16.dpToPx()) + assertThat(endPaddingPx).isEqualTo(16.dpToPx()) + assertThat(gutterPx).isEqualTo(16.dpToPx()) + assertThat(cellSizePx).isEqualTo(102.dpToPx()) + } + } + + @Test + fun validate_matchHeightWorkspace() { + // Hotseat is roughly 495px on a real device, it doesn't need to be precise on unit tests + val hotseatSize = 495 + val statusBarHeight = deviceSpec.statusBarNaturalPx + val availableHeight = deviceSpec.naturalSize.second - statusBarHeight - hotseatSize + val rows = 5 + + // Loading workspace specs + val resourceHelperWorkspace = TestResourceHelper(context!!, R.xml.valid_workspace_file) + val workspaceSpecs = WorkspaceSpecs.create(resourceHelperWorkspace) + + // Loading folders specs + val resourceHelperFolder = TestResourceHelper(context!!, R.xml.valid_folders_specs) + val folderSpecs = FolderSpecs.create(resourceHelperFolder) + + assertThat(folderSpecs.heightSpecs.size).isEqualTo(1) + assertThat(folderSpecs.heightSpecs[0].cellSize.matchWorkspace).isEqualTo(true) + + // Validate height spec + val calculatedWorkspace = workspaceSpecs.getCalculatedHeightSpec(rows, availableHeight) + val calculatedFolderSpec = + folderSpecs.getCalculatedHeightSpec(rows, availableHeight, calculatedWorkspace) + with(calculatedFolderSpec) { + assertThat(availableSpace).isEqualTo(availableHeight) + assertThat(cells).isEqualTo(rows) + assertThat(startPaddingPx).isEqualTo(24.dpToPx()) + assertThat(endPaddingPx).isEqualTo(64.dpToPx()) + assertThat(gutterPx).isEqualTo(16.dpToPx()) + assertThat(cellSizePx).isEqualTo(calculatedWorkspace.cellSizePx) + } + } +} diff --git a/tests/src/com/android/launcher3/responsive/CalculatedHotseatSpecTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedHotseatSpecTest.kt new file mode 100644 index 0000000000..0ecf7bae5a --- /dev/null +++ b/tests/src/com/android/launcher3/responsive/CalculatedHotseatSpecTest.kt @@ -0,0 +1,75 @@ +/* + * 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.launcher3.responsive + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.tests.R as TestR +import com.android.launcher3.util.TestResourceHelper +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class CalculatedHotseatSpecTest : AbstractDeviceProfileTest() { + override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + + /** + * This test tests: + * - (height spec) gets the correct breakpoint from the XML - skips the first breakpoint + */ + @Test + fun normalPhone_returnsSecondBreakpointSpec() { + val deviceSpec = deviceSpecs["phone"]!! + initializeVarsForPhone(deviceSpec) + + // Hotseat uses the whole device height + val availableHeight = deviceSpec.naturalSize.second + + val hotseatSpecs = + HotseatSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_hotseat_file)) + val heightSpec = hotseatSpecs.getCalculatedHeightSpec(availableHeight) + + assertThat(heightSpec.availableSpace).isEqualTo(availableHeight) + assertThat(heightSpec.hotseatQsbSpace).isEqualTo(95) + } + + /** + * This test tests: + * - (height spec) gets the correct breakpoint from the XML - use the first breakpoint + */ + @Test + fun smallPhone_returnsFirstBreakpointSpec() { + val deviceSpec = deviceSpecs["phone"]!! + deviceSpec.densityDpi = 540 // larger display size + initializeVarsForPhone(deviceSpec) + + // Hotseat uses the whole device height + val availableHeight = deviceSpec.naturalSize.second + + val hotseatSpecs = + HotseatSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_hotseat_file)) + val heightSpec = hotseatSpecs.getCalculatedHeightSpec(availableHeight) + + assertThat(heightSpec.availableSpace).isEqualTo(availableHeight) + assertThat(heightSpec.hotseatQsbSpace).isEqualTo(81) + } +} diff --git a/tests/src/com/android/launcher3/workspace/CalculatedWorkspaceSpecTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedWorkspaceSpecTest.kt index 7f03ba2f77..0af694e000 100644 --- a/tests/src/com/android/launcher3/workspace/CalculatedWorkspaceSpecTest.kt +++ b/tests/src/com/android/launcher3/responsive/CalculatedWorkspaceSpecTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3.workspace +package com.android.launcher3.responsive import android.content.Context import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -49,7 +49,7 @@ class CalculatedWorkspaceSpecTest : AbstractDeviceProfileTest() { val availableHeight = deviceSpec.naturalSize.second - deviceSpec.statusBarNaturalPx - 495 val workspaceSpecs = - WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.valid_workspace_file)) + WorkspaceSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_workspace_file)) val widthSpec = workspaceSpecs.getCalculatedWidthSpec(4, availableWidth) val heightSpec = workspaceSpecs.getCalculatedHeightSpec(5, availableHeight) @@ -86,7 +86,7 @@ class CalculatedWorkspaceSpecTest : AbstractDeviceProfileTest() { val availableHeight = deviceSpec.naturalSize.second - deviceSpec.statusBarNaturalPx - 640 val workspaceSpecs = - WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.valid_workspace_file)) + WorkspaceSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_workspace_file)) val widthSpec = workspaceSpecs.getCalculatedWidthSpec(4, availableWidth) val heightSpec = workspaceSpecs.getCalculatedHeightSpec(5, availableHeight) diff --git a/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt b/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt new file mode 100644 index 0000000000..4b05949e35 --- /dev/null +++ b/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt @@ -0,0 +1,251 @@ +/* + * 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.launcher3.responsive + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.responsive.ResponsiveSpec.SpecType +import com.android.launcher3.tests.R +import com.android.launcher3.util.TestResourceHelper +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class FolderSpecsTest : AbstractDeviceProfileTest() { + override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + + @Before + fun setup() { + initializeVarsForPhone(deviceSpecs["tablet"]!!) + } + + @Test + fun parseValidFile() { + val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs) + val folderSpecs = FolderSpecs.create(resourceHelper) + + val sizeSpec16 = SizeSpec(16f.dpToPx()) + val widthSpecsExpected = + listOf( + FolderSpec( + maxAvailableSize = 800.dpToPx(), + specType = SpecType.WIDTH, + startPadding = sizeSpec16, + endPadding = sizeSpec16, + gutter = sizeSpec16, + cellSize = SizeSpec(matchWorkspace = true) + ), + FolderSpec( + maxAvailableSize = 9999.dpToPx(), + specType = SpecType.WIDTH, + startPadding = sizeSpec16, + endPadding = sizeSpec16, + gutter = sizeSpec16, + cellSize = SizeSpec(102f.dpToPx()) + ) + ) + + val heightSpecsExpected = + FolderSpec( + maxAvailableSize = 9999.dpToPx(), + specType = SpecType.HEIGHT, + startPadding = SizeSpec(24f.dpToPx()), + endPadding = SizeSpec(64f.dpToPx()), + gutter = sizeSpec16, + cellSize = SizeSpec(matchWorkspace = true) + ) + + assertThat(folderSpecs.widthSpecs.size).isEqualTo(widthSpecsExpected.size) + assertThat(folderSpecs.widthSpecs[0]).isEqualTo(widthSpecsExpected[0]) + assertThat(folderSpecs.widthSpecs[1]).isEqualTo(widthSpecsExpected[1]) + + assertThat(folderSpecs.heightSpecs.size).isEqualTo(1) + assertThat(folderSpecs.heightSpecs[0]).isEqualTo(heightSpecsExpected) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_missingTag_throwsError() { + val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_1) + FolderSpecs.create(resourceHelper) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_moreThanOneValuePerTag_throwsError() { + val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_2) + FolderSpecs.create(resourceHelper) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_valueBiggerThan1_throwsError() { + val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_3) + FolderSpecs.create(resourceHelper) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_missingSpecs_throwsError() { + val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_4) + FolderSpecs.create(resourceHelper) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_missingWidthBreakpoint_throwsError() { + val availableSpace = 900.dpToPx() + val cells = 3 + + val workspaceSpec = + WorkspaceSpec( + maxAvailableSize = availableSpace, + specType = SpecType.WIDTH, + startPadding = SizeSpec(fixedSize = 10f), + endPadding = SizeSpec(fixedSize = 10f), + gutter = SizeSpec(fixedSize = 10f), + cellSize = SizeSpec(fixedSize = 10f) + ) + val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec) + + val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_5) + val folderSpecs = FolderSpecs.create(resourceHelper) + folderSpecs.getCalculatedWidthSpec(cells, availableSpace, calculatedWorkspaceSpec) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_missingHeightBreakpoint_throwsError() { + val availableSpace = 900.dpToPx() + val cells = 3 + + val workspaceSpec = + WorkspaceSpec( + maxAvailableSize = availableSpace, + specType = SpecType.HEIGHT, + startPadding = SizeSpec(fixedSize = 10f), + endPadding = SizeSpec(fixedSize = 10f), + gutter = SizeSpec(fixedSize = 10f), + cellSize = SizeSpec(fixedSize = 10f) + ) + val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec) + + val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_5) + val folderSpecs = FolderSpecs.create(resourceHelper) + folderSpecs.getCalculatedHeightSpec(cells, availableSpace, calculatedWorkspaceSpec) + } + + @Test + fun retrievesCalculatedWidthSpec() { + val availableSpace = 800.dpToPx() + val cells = 3 + + val workspaceSpec = + WorkspaceSpec( + maxAvailableSize = availableSpace, + specType = SpecType.WIDTH, + startPadding = SizeSpec(fixedSize = 10f), + endPadding = SizeSpec(fixedSize = 10f), + gutter = SizeSpec(fixedSize = 10f), + cellSize = SizeSpec(fixedSize = 10f) + ) + val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec) + + val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs) + val folderSpecs = FolderSpecs.create(resourceHelper) + val calculatedWidthSpec = + folderSpecs.getCalculatedWidthSpec(cells, availableSpace, calculatedWorkspaceSpec) + + assertThat(calculatedWidthSpec.cells).isEqualTo(cells) + assertThat(calculatedWidthSpec.availableSpace).isEqualTo(availableSpace) + assertThat(calculatedWidthSpec.startPaddingPx).isEqualTo(16.dpToPx()) + assertThat(calculatedWidthSpec.endPaddingPx).isEqualTo(16.dpToPx()) + assertThat(calculatedWidthSpec.gutterPx).isEqualTo(16.dpToPx()) + assertThat(calculatedWidthSpec.cellSizePx).isEqualTo(calculatedWorkspaceSpec.cellSizePx) + } + + @Test(expected = IllegalStateException::class) + fun retrievesCalculatedWidthSpec_invalidCalculatedWorkspaceSpecType_throwsError() { + val availableSpace = 10.dpToPx() + val cells = 3 + + val workspaceSpec = + WorkspaceSpec( + maxAvailableSize = availableSpace, + specType = SpecType.HEIGHT, + startPadding = SizeSpec(fixedSize = 10f), + endPadding = SizeSpec(fixedSize = 10f), + gutter = SizeSpec(fixedSize = 10f), + cellSize = SizeSpec(fixedSize = 10f) + ) + val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec) + + val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs) + val folderSpecs = FolderSpecs.create(resourceHelper) + folderSpecs.getCalculatedWidthSpec(cells, availableSpace, calculatedWorkspaceSpec) + } + + @Test + fun retrievesCalculatedHeightSpec() { + val availableSpace = 700.dpToPx() + val cells = 3 + + val workspaceSpec = + WorkspaceSpec( + maxAvailableSize = availableSpace, + specType = SpecType.HEIGHT, + startPadding = SizeSpec(fixedSize = 10f), + endPadding = SizeSpec(fixedSize = 10f), + gutter = SizeSpec(fixedSize = 10f), + cellSize = SizeSpec(fixedSize = 10f) + ) + val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec) + + val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs) + val folderSpecs = FolderSpecs.create(resourceHelper) + val calculatedHeightSpec = + folderSpecs.getCalculatedHeightSpec(cells, availableSpace, calculatedWorkspaceSpec) + + assertThat(calculatedHeightSpec.cells).isEqualTo(cells) + assertThat(calculatedHeightSpec.availableSpace).isEqualTo(availableSpace) + assertThat(calculatedHeightSpec.startPaddingPx).isEqualTo(24.dpToPx()) + assertThat(calculatedHeightSpec.endPaddingPx).isEqualTo(64.dpToPx()) + assertThat(calculatedHeightSpec.gutterPx).isEqualTo(16.dpToPx()) + assertThat(calculatedHeightSpec.cellSizePx).isEqualTo(calculatedWorkspaceSpec.cellSizePx) + } + + @Test(expected = IllegalStateException::class) + fun retrievesCalculatedHeightSpec_invalidCalculatedWorkspaceSpecType_throwsError() { + val availableSpace = 10.dpToPx() + val cells = 3 + + val workspaceSpec = + WorkspaceSpec( + maxAvailableSize = availableSpace, + specType = SpecType.WIDTH, + startPadding = SizeSpec(fixedSize = 10f), + endPadding = SizeSpec(fixedSize = 10f), + gutter = SizeSpec(fixedSize = 10f), + cellSize = SizeSpec(fixedSize = 10f) + ) + val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec) + + val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs) + val folderSpecs = FolderSpecs.create(resourceHelper) + folderSpecs.getCalculatedHeightSpec(cells, availableSpace, calculatedWorkspaceSpec) + } +} diff --git a/tests/src/com/android/launcher3/responsive/HotseatSpecsTest.kt b/tests/src/com/android/launcher3/responsive/HotseatSpecsTest.kt new file mode 100644 index 0000000000..c764e47526 --- /dev/null +++ b/tests/src/com/android/launcher3/responsive/HotseatSpecsTest.kt @@ -0,0 +1,71 @@ +/* + * 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.launcher3.responsive + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.tests.R as TestR +import com.android.launcher3.util.TestResourceHelper +import com.android.systemui.util.dpToPx +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class HotseatSpecsTest : AbstractDeviceProfileTest() { + override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + + @Before + fun setup() { + initializeVarsForPhone(deviceSpecs["phone"]!!) + } + + @Test + fun parseValidFile() { + val hotseatSpecs = + HotseatSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_hotseat_file)) + assertThat(hotseatSpecs.specs.size).isEqualTo(2) + + val expectedSpecs = + listOf( + HotseatSpec( + maxAvailableSize = 847.dpToPx(), + specType = ResponsiveSpec.SpecType.HEIGHT, + hotseatQsbSpace = SizeSpec(24f.dpToPx()) + ), + HotseatSpec( + maxAvailableSize = 9999.dpToPx(), + specType = ResponsiveSpec.SpecType.HEIGHT, + hotseatQsbSpace = SizeSpec(36f.dpToPx()) + ), + ) + + assertThat(hotseatSpecs.specs.size).isEqualTo(expectedSpecs.size) + assertThat(hotseatSpecs.specs[0]).isEqualTo(expectedSpecs[0]) + assertThat(hotseatSpecs.specs[1]).isEqualTo(expectedSpecs[1]) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_spaceIsNotFixedSize_throwsError() { + HotseatSpecs.create(TestResourceHelper(context!!, TestR.xml.invalid_hotseat_file_case_1)) + } +} diff --git a/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt b/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt new file mode 100644 index 0000000000..8ca07c6d96 --- /dev/null +++ b/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt @@ -0,0 +1,158 @@ +/* + * 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.launcher3.responsive + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.AbstractDeviceProfileTest +import com.google.common.truth.Truth.assertThat +import kotlin.math.roundToInt +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class SizeSpecTest : AbstractDeviceProfileTest() { + override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + + @Before + fun setup() { + initializeVarsForPhone(deviceSpecs["phone"]!!) + } + + @Test + fun valid_values() { + val combinations = + listOf( + SizeSpec(100f, 0f, 0f, false), + SizeSpec(0f, 1f, 0f, false), + SizeSpec(0f, 0f, 1f, false), + SizeSpec(0f, 0f, 0f, false), + SizeSpec(0f, 0f, 0f, true), + SizeSpec(100f, 0f, 0f, false, 100), + SizeSpec(0f, 1f, 0f, false, 100), + SizeSpec(0f, 0f, 1f, false, 100), + SizeSpec(0f, 0f, 0f, false, 100), + SizeSpec(0f, 0f, 0f, true, 100) + ) + + for (instance in combinations) { + assertThat(instance.isValid()).isEqualTo(true) + } + } + + @Test + fun validate_getCalculatedValue() { + val availableSpace = 100 + val matchWorkspaceValue = 101 + val combinations = + listOf( + SizeSpec(100f) to 100, + SizeSpec(ofAvailableSpace = .5f) to (availableSpace * .5f).roundToInt(), + SizeSpec(ofRemainderSpace = .5f) to 0, + SizeSpec(matchWorkspace = true) to matchWorkspaceValue, + // Restricts max size up to 10 (calculated value > 10) + SizeSpec(100f, maxSize = 10) to 10, + SizeSpec(ofAvailableSpace = .5f, maxSize = 10) to 10, + SizeSpec(ofRemainderSpace = .5f, maxSize = 10) to 0, + SizeSpec(matchWorkspace = true, maxSize = 10) to 10 + ) + + for ((sizeSpec, expectedValue) in combinations) { + val value = sizeSpec.getCalculatedValue(availableSpace, matchWorkspaceValue) + assertThat(value).isEqualTo(expectedValue) + } + } + + @Test + fun validate_getRemainderSpaceValue() { + val remainderSpace = 100 + val defaultValue = 50 + val combinations = + listOf( + SizeSpec(100f) to defaultValue, + SizeSpec(ofAvailableSpace = .5f) to defaultValue, + SizeSpec(ofRemainderSpace = .5f) to (remainderSpace * .5f).roundToInt(), + SizeSpec(matchWorkspace = true) to defaultValue, + // Restricts max size up to 10 (defaultValue > 10) + SizeSpec(100f, maxSize = 10) to 10, + SizeSpec(ofAvailableSpace = .5f, maxSize = 10) to 10, + SizeSpec(ofRemainderSpace = .5f, maxSize = 10) to 10, + SizeSpec(matchWorkspace = true, maxSize = 10) to 10, + ) + + for ((sizeSpec, expectedValue) in combinations) { + val value = sizeSpec.getRemainderSpaceValue(remainderSpace, defaultValue) + assertThat(value).isEqualTo(expectedValue) + } + } + + @Test + fun multiple_values_assigned() { + val combinations = + listOf( + SizeSpec(1f, 1f, 0f, false), + SizeSpec(1f, 0f, 1f, false), + SizeSpec(1f, 0f, 0f, true), + SizeSpec(0f, 1f, 1f, false), + SizeSpec(0f, 1f, 0f, true), + SizeSpec(0f, 0f, 1f, true), + SizeSpec(1f, 1f, 1f, true) + ) + + for (instance in combinations) { + assertThat(instance.isValid()).isEqualTo(false) + } + } + + @Test + fun invalid_values() { + val combinations = + listOf( + SizeSpec(-1f, 0f, 0f, false), + SizeSpec(0f, 1.1f, 0f, false), + SizeSpec(0f, -0.1f, 0f, false), + SizeSpec(0f, 0f, 1.1f, false), + SizeSpec(0f, 0f, -0.1f, false), + SizeSpec(0f, 0f, 0f, false, -10), + SizeSpec(50f, 0f, 0f, false, 10) + ) + + for (instance in combinations) { + assertThat(instance.isValid()).isEqualTo(false) + } + } + + @Test + fun onlyFixedSize() { + assertThat(SizeSpec(fixedSize = 16f).onlyFixedSize()).isEqualTo(true) + + val combinations = + listOf( + SizeSpec(0f, 1.1f, 0f, false), + SizeSpec(0f, 0f, 1.1f, false), + SizeSpec(0f, 0f, 0f, true) + ) + + for (instance in combinations) { + assertThat(instance.onlyFixedSize()).isEqualTo(false) + } + } +} diff --git a/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt b/tests/src/com/android/launcher3/responsive/WorkspaceSpecsTest.kt index 9cd0a2e4a3..0364069535 100644 --- a/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt +++ b/tests/src/com/android/launcher3/responsive/WorkspaceSpecsTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3.workspace +package com.android.launcher3.responsive import android.content.Context import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -41,99 +41,144 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() { @Test fun parseValidFile() { val workspaceSpecs = - WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.valid_workspace_file)) - assertThat(workspaceSpecs.workspaceHeightSpecList.size).isEqualTo(3) - assertThat(workspaceSpecs.workspaceHeightSpecList[0].toString()) + WorkspaceSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_workspace_file)) + assertThat(workspaceSpecs.heightSpecs.size).isEqualTo(3) + assertThat(workspaceSpecs.heightSpecs[0].toString()) .isEqualTo( "WorkspaceSpec(" + "maxAvailableSize=1533, " + "specType=HEIGHT, " + "startPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "endPadding=SizeSpec(fixedSize=84.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "gutter=SizeSpec(fixedSize=42.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "cellSize=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.15808, " + - "ofRemainderSpace=0.0)" + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647)" + ")" ) - assertThat(workspaceSpecs.workspaceHeightSpecList[1].toString()) + assertThat(workspaceSpecs.heightSpecs[1].toString()) .isEqualTo( "WorkspaceSpec(" + "maxAvailableSize=1607, " + "specType=HEIGHT, " + "startPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "endPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=1.0), " + + "ofRemainderSpace=1.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "gutter=SizeSpec(fixedSize=42.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "cellSize=SizeSpec(fixedSize=273.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0)" + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647)" + ")" ) - assertThat(workspaceSpecs.workspaceHeightSpecList[2].toString()) + assertThat(workspaceSpecs.heightSpecs[2].toString()) .isEqualTo( "WorkspaceSpec(" + "maxAvailableSize=26247, " + "specType=HEIGHT, " + "startPadding=SizeSpec(fixedSize=21.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "endPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=1.0), " + + "ofRemainderSpace=1.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "gutter=SizeSpec(fixedSize=42.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "cellSize=SizeSpec(fixedSize=273.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0)" + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647)" + ")" ) - assertThat(workspaceSpecs.workspaceWidthSpecList.size).isEqualTo(1) - assertThat(workspaceSpecs.workspaceWidthSpecList[0].toString()) + assertThat(workspaceSpecs.widthSpecs.size).isEqualTo(1) + assertThat(workspaceSpecs.widthSpecs[0].toString()) .isEqualTo( "WorkspaceSpec(" + "maxAvailableSize=26247, " + "specType=WIDTH, " + "startPadding=SizeSpec(fixedSize=58.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "endPadding=SizeSpec(fixedSize=58.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "gutter=SizeSpec(fixedSize=42.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "cellSize=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.25)" + + "ofRemainderSpace=0.25, " + + "matchWorkspace=false, " + + "maxSize=2147483647)" + ")" ) } @Test(expected = IllegalStateException::class) fun parseInvalidFile_missingTag_throwsError() { - WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_1)) + WorkspaceSpecs.create( + TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_1) + ) } @Test(expected = IllegalStateException::class) fun parseInvalidFile_moreThanOneValuePerTag_throwsError() { - WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_2)) + WorkspaceSpecs.create( + TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_2) + ) } @Test(expected = IllegalStateException::class) fun parseInvalidFile_valueBiggerThan1_throwsError() { - WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_3)) + WorkspaceSpecs.create( + TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_3) + ) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_matchWorkspace_true_throwsError() { + WorkspaceSpecs.create( + TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_4) + ) } } diff --git a/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java b/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java index 7e9d9da5c2..c7431f20dd 100644 --- a/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java +++ b/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java @@ -184,7 +184,7 @@ public final class SecondaryDisplayLauncherTest extends AbstractLauncherUiTest { mStartPoint = icon.getVisibleCenter(); mEndPoint = new Point(mStartPoint.x, mStartPoint.y); mLauncher.sendPointer(mDownTime, mDownTime, ACTION_DOWN, mStartPoint, - LauncherInstrumentation.GestureScope.INSIDE); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); assertThat(findObjectByResourceName("popup_container")).isNotNull(); return appName; } @@ -206,7 +206,7 @@ public final class SecondaryDisplayLauncherTest extends AbstractLauncherUiTest { mStartPoint = icon.getVisibleCenter(); mEndPoint = new Point(mStartPoint.x, mStartPoint.y); mLauncher.sendPointer(mDownTime, mDownTime, ACTION_DOWN, mStartPoint, - LauncherInstrumentation.GestureScope.INSIDE); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); assertThat(findObjectByResourceName("popup_container")).isNotNull(); return appName; } @@ -214,12 +214,12 @@ public final class SecondaryDisplayLauncherTest extends AbstractLauncherUiTest { private void moveAppToCenterOfScreen() { mEndPoint.set(mDevice.getDisplayWidth() / 2, mDevice.getDisplayHeight() / 2); mLauncher.movePointer(mDownTime, SystemClock.uptimeMillis(), DRAG_TIME_MS, true, - mStartPoint, mEndPoint, LauncherInstrumentation.GestureScope.INSIDE); + mStartPoint, mEndPoint, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); } private void dropApp() { mLauncher.sendPointer(mDownTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, - mEndPoint, LauncherInstrumentation.GestureScope.INSIDE); + mEndPoint, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); } private void removeAppByName(String appName) { diff --git a/tests/src/com/android/launcher3/tapl/TaplUtilityTests.java b/tests/src/com/android/launcher3/tapl/TaplUtilityTests.java new file mode 100644 index 0000000000..15db1d887c --- /dev/null +++ b/tests/src/com/android/launcher3/tapl/TaplUtilityTests.java @@ -0,0 +1,38 @@ +/* + * 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.launcher3.tapl; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class TaplUtilityTests { + + @Test + public void testNewStringWithRegex() { + assertTrue(AppIcon.makeMultilinePattern("Play Store") + .matcher("Play Store has 7 notifications").matches()); + assertTrue(AppIcon.makeMultilinePattern("Play Store") + .matcher("Play Store!").matches()); + assertFalse(AppIcon.makeMultilinePattern("Play Store") + .matcher("play store").matches()); + assertFalse(AppIcon.makeMultilinePattern("Play Store") + .matcher("").matches()); + assertTrue(AppIcon.makeMultilinePattern("Play Store") + .matcher("Play \n Store").matches()); + } +} diff --git a/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java b/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java index d3ce67c81c..81a59b9a45 100644 --- a/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java +++ b/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java @@ -69,7 +69,10 @@ public class BaseTestingActivity extends Activity implements View.OnClickListene mView.setBackgroundColor(Color.BLUE); setContentView(mView); - registerReceiver(mCommandReceiver, new IntentFilter(mAction + SUFFIX_COMMAND)); + registerReceiver( + mCommandReceiver, + new IntentFilter(mAction + SUFFIX_COMMAND), + RECEIVER_EXPORTED); } protected void addButton(String title, String method) { diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 604fe42625..5240e6a02b 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -17,6 +17,7 @@ package com.android.launcher3.ui; import static androidx.test.InstrumentationRegistry.getInstrumentation; +import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING; import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; @@ -49,11 +50,8 @@ import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.Until; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; -import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.tapl.HomeAllApps; import com.android.launcher3.tapl.HomeAppIcon; @@ -64,10 +62,9 @@ import com.android.launcher3.testcomponent.TestCommandReceiver; import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.SimpleBroadcastReceiver; +import com.android.launcher3.util.TestUtil; import com.android.launcher3.util.Wait; -import com.android.launcher3.util.WidgetUtils; import com.android.launcher3.util.rule.FailureWatcher; -import com.android.launcher3.util.rule.LauncherActivityRule; import com.android.launcher3.util.rule.SamplerRule; import com.android.launcher3.util.rule.ScreenRecordRule; import com.android.launcher3.util.rule.ShellCommandRule; @@ -82,10 +79,6 @@ import org.junit.rules.RuleChain; import org.junit.rules.TestRule; import java.io.IOException; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -102,12 +95,12 @@ public abstract class AbstractLauncherUiTest { public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10); public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5; - public static final long DEFAULT_UI_TIMEOUT = 10000; + public static final long DEFAULT_UI_TIMEOUT = TestUtil.DEFAULT_UI_TIMEOUT; private static final String TAG = "AbstractLauncherUiTest"; private static boolean sDumpWasGenerated = false; private static boolean sActivityLeakReported = false; - private static boolean sSeenKeygard = false; + private static boolean sSeenKeyguard = false; private static final String SYSTEMUI_PACKAGE = "com.android.systemui"; @@ -188,8 +181,6 @@ public abstract class AbstractLauncherUiTest { mLauncher.setOnLauncherCrashed(() -> mLauncherPid = 0); } - protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule(); - @Rule public ShellCommandRule mDisableHeadsUpNotification = ShellCommandRule.disableHeadsUpNotification(); @@ -209,18 +200,13 @@ public abstract class AbstractLauncherUiTest { mTargetContext.unregisterReceiver(broadcastReceiver); } - // Annotation for tests that need to be run in portrait and landscape modes. - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.METHOD) - protected @interface PortraitLandscape { - } - protected TestRule getRulesInsideActivityMonitor() { - final ViewCaptureRule viewCaptureRule = new ViewCaptureRule(); + final ViewCaptureRule viewCaptureRule = new ViewCaptureRule( + Launcher.ACTIVITY_TRACKER::getCreatedActivity); final RuleChain inner = RuleChain .outerRule(new PortraitLandscapeRunner(this)) - .around(viewCaptureRule) - .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture())); + .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData)) + .around(viewCaptureRule); return TestHelpers.isInLauncherProcess() ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner) @@ -231,7 +217,6 @@ public abstract class AbstractLauncherUiTest { public TestRule mOrderSensitiveRules = RuleChain .outerRule(new SamplerRule()) .around(new TestStabilityRule()) - .around(mActivityMonitor) .around(getRulesInsideActivityMonitor()); public UiDevice getDevice() { @@ -276,9 +261,9 @@ public abstract class AbstractLauncherUiTest { } private static void verifyKeyguardInvisible() { - final boolean keyguardAlreadyVisible = sSeenKeygard; + final boolean keyguardAlreadyVisible = sSeenKeyguard; - sSeenKeygard = sSeenKeygard + sSeenKeyguard = sSeenKeyguard || !TestHelpers.wait( Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000); @@ -286,7 +271,7 @@ public abstract class AbstractLauncherUiTest { "Keyguard is visible, which is likely caused by a crash in SysUI, seeing keyguard" + " for the first time = " + !keyguardAlreadyVisible, - sSeenKeygard); + sSeenKeyguard); } @After @@ -303,46 +288,20 @@ public abstract class AbstractLauncherUiTest { } } - protected void clearLauncherData() { - mLauncher.clearLauncherData(); - mLauncher.waitForLauncherInitialized(); - } - - /** - * Removes all icons from homescreen and hotseat. - */ - public void clearHomescreen() { - LauncherSettings.Settings.call(mTargetContext.getContentResolver(), - LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); - LauncherSettings.Settings.call(mTargetContext.getContentResolver(), - LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG); - resetLoaderState(); + protected void reinitializeLauncherData() { + reinitializeLauncherData(false); } - protected void resetLoaderState() { - try { - mMainThreadExecutor.execute( - () -> LauncherAppState.getInstance( - mTargetContext).getModel().forceReload()); - } catch (Throwable t) { - throw new IllegalArgumentException(t); + protected void reinitializeLauncherData(boolean clearWorkspace) { + if (clearWorkspace) { + mLauncher.clearLauncherData(); + } else { + mLauncher.reinitializeLauncherData(); } mLauncher.waitForLauncherInitialized(); } /** - * Adds {@param item} on the homescreen on the 0th screen - */ - public void addItemToScreen(ItemInfo item) { - WidgetUtils.addItemToScreen(item, mTargetContext); - resetLoaderState(); - - // Launch the home activity - mDevice.pressHome(); - mLauncher.waitForLauncherInitialized(); - } - - /** * Runs the callback on the UI thread and returns the result. */ protected <T> T getOnUiThread(final Callable<T> callback) { @@ -360,7 +319,7 @@ public abstract class AbstractLauncherUiTest { protected <T> T getFromLauncher(Function<Launcher, T> f) { if (!TestHelpers.isInLauncherProcess()) return null; - return getOnUiThread(() -> f.apply(mActivityMonitor.getActivity())); + return getOnUiThread(() -> f.apply(Launcher.ACTIVITY_TRACKER.getCreatedActivity())); } protected void executeOnLauncher(Consumer<Launcher> f) { @@ -648,6 +607,8 @@ public abstract class AbstractLauncherUiTest { protected HomeAppIcon createShortcutIfNotExist(String name, int cellX, int cellY) { HomeAppIcon homeAppIcon = mLauncher.getWorkspace().tryGetWorkspaceAppIcon(name); + Log.d(ICON_MISSING, "homeAppIcon: " + homeAppIcon + " name: " + name + + " cell: " + cellX + ", " + cellY); if (homeAppIcon == null) { HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); allApps.freeze(); diff --git a/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java b/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java index fdba4eb2a3..ba17fdc8dd 100644 --- a/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java +++ b/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java @@ -18,6 +18,8 @@ package com.android.launcher3.ui; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static com.android.launcher3.BubbleTextView.DISPLAY_ALL_APPS; +import static com.android.launcher3.BubbleTextView.DISPLAY_PREDICTION_ROW; import static com.android.launcher3.config.FeatureFlags.ENABLE_TWOLINE_ALLAPPS; import static org.junit.Assert.assertEquals; @@ -79,7 +81,6 @@ public class BubbleTextViewTest { mContext = new ActivityContextWrapper(getApplicationContext()); mBubbleTextView = new BubbleTextView(mContext); mBubbleTextView.reset(); - mBubbleTextView.setDisplayAllApps(); BubbleTextView testView = new BubbleTextView(mContext); testView.setTypeface(Typeface.MONOSPACE); @@ -104,6 +105,7 @@ public class BubbleTextViewTest { public void testEmptyString_flagOn() { try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) { mItemInfoWithIcon.title = EMPTY_STRING; + mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.applyLabel(mItemInfoWithIcon); mBubbleTextView.setTypeface(Typeface.MONOSPACE); mBubbleTextView.measure(mLimitedWidth, 0); @@ -118,6 +120,7 @@ public class BubbleTextViewTest { public void testEmptyString_flagOff() { try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, false)) { mItemInfoWithIcon.title = EMPTY_STRING; + mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.applyLabel(mItemInfoWithIcon); mBubbleTextView.setTypeface(Typeface.MONOSPACE); mBubbleTextView.measure(mLimitedWidth, 0); @@ -134,6 +137,7 @@ public class BubbleTextViewTest { // test string: "Battery Stats" mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); + mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); mBubbleTextView.measure(mLimitedWidth, 0); mBubbleTextView.onPreDraw(); @@ -149,6 +153,7 @@ public class BubbleTextViewTest { // test string: "Battery Stats" mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); + mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); mBubbleTextView.measure(mLimitedWidth, 0); mBubbleTextView.onPreDraw(); @@ -164,6 +169,7 @@ public class BubbleTextViewTest { // test string: "flutterappflorafy" mItemInfoWithIcon.title = TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); + mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); mBubbleTextView.measure(mLimitedWidth, 0); mBubbleTextView.onPreDraw(); @@ -179,6 +185,7 @@ public class BubbleTextViewTest { // test string: "flutterappflorafy" mItemInfoWithIcon.title = TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); + mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); mBubbleTextView.measure(mLimitedWidth, 0); mBubbleTextView.onPreDraw(); @@ -194,6 +201,7 @@ public class BubbleTextViewTest { // test string: "System UWB Field Test" mItemInfoWithIcon.title = TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); + mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); mBubbleTextView.measure(mLimitedWidth, 0); mBubbleTextView.onPreDraw(); @@ -209,6 +217,7 @@ public class BubbleTextViewTest { // test string: "System UWB Field Test" mItemInfoWithIcon.title = TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); + mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); mBubbleTextView.measure(mLimitedWidth, 0); mBubbleTextView.onPreDraw(); @@ -224,6 +233,7 @@ public class BubbleTextViewTest { // test string: "LEGO®Builder" mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); + mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); mBubbleTextView.measure(mLimitedWidth, 0); mBubbleTextView.onPreDraw(); @@ -239,6 +249,7 @@ public class BubbleTextViewTest { // test string: "LEGO®Builder" mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); + mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); mBubbleTextView.measure(mLimitedWidth, 0); mBubbleTextView.onPreDraw(); @@ -291,4 +302,20 @@ public class BubbleTextViewTest { breakPoints); assertEquals(TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT_RESULT, newString); } + + @Test + public void testEnsurePredictionRowIsOneLine() { + try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) { + // test string: "Battery Stats" + mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT; + mBubbleTextView.setDisplay(DISPLAY_PREDICTION_ROW); + mBubbleTextView.applyLabel(mItemInfoWithIcon); + mBubbleTextView.setTypeface(Typeface.MONOSPACE); + mBubbleTextView.measure(mLimitedWidth, 0); + mBubbleTextView.onPreDraw(); + assertEquals(ONE_LINE, mBubbleTextView.getLineCount()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java index 266f0aeb1d..f0875f8104 100644 --- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java +++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java @@ -9,10 +9,21 @@ import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; -class PortraitLandscapeRunner implements TestRule { +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public class PortraitLandscapeRunner implements TestRule { private static final String TAG = "PortraitLandscapeRunner"; private AbstractLauncherUiTest mTest; + // Annotation for tests that need to be run in portrait and landscape modes. + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface PortraitLandscape { + } + public PortraitLandscapeRunner(AbstractLauncherUiTest test) { mTest = test; } @@ -20,7 +31,7 @@ class PortraitLandscapeRunner implements TestRule { @Override public Statement apply(Statement base, Description description) { if (!TestHelpers.isInLauncherProcess() || - description.getAnnotation(AbstractLauncherUiTest.PortraitLandscape.class) == null) { + description.getAnnotation(PortraitLandscape.class) == null) { return base; } diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 0b106037ce..45b01f4a20 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -18,12 +18,15 @@ package com.android.launcher3.ui; import static androidx.test.InstrumentationRegistry.getInstrumentation; +import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING; +import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; +import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; @@ -54,10 +57,13 @@ import com.android.launcher3.tapl.HomeAppIcon; import com.android.launcher3.tapl.HomeAppIconMenuItem; import com.android.launcher3.tapl.Widgets; import com.android.launcher3.tapl.Workspace; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; import com.android.launcher3.util.LauncherLayoutBuilder; import com.android.launcher3.util.TestUtil; +import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; import com.android.launcher3.util.rule.TISBindRule; +import com.android.launcher3.util.rule.TestStabilityRule.Stability; import com.android.launcher3.widget.picker.WidgetsFullSheet; import com.android.launcher3.widget.picker.WidgetsRecyclerView; @@ -94,7 +100,12 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } public static void initialize(AbstractLauncherUiTest test) throws Exception { - test.clearLauncherData(); + initialize(test, false); + } + + public static void initialize( + AbstractLauncherUiTest test, boolean clearWorkspace) throws Exception { + test.reinitializeLauncherData(clearWorkspace); test.mDevice.pressHome(); test.waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null); test.waitForState("Launcher internal state didn't switch to Home", @@ -147,7 +158,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } @Test - @ScreenRecord public void testPressHomeOnAllAppsContextMenu() throws Exception { final AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); allApps.freeze(); @@ -221,7 +231,18 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @PortraitLandscape public void testAllAppsSwitchToWorkspace() { assertNotNull("switchToWorkspace() returned null", - mLauncher.getWorkspace().switchToAllApps().switchToWorkspace()); + mLauncher.getWorkspace().switchToAllApps() + .switchToWorkspace(/* swipeDown= */ true)); + assertTrue("Launcher internal state is not Workspace", + isInState(() -> LauncherState.NORMAL)); + } + + @Test + @PortraitLandscape + public void testAllAppsSwipeUpToWorkspace() { + assertNotNull("testAllAppsSwipeUpToWorkspace() returned null", + mLauncher.getWorkspace().switchToAllApps() + .switchToWorkspace(/* swipeDown= */ false)); assertTrue("Launcher internal state is not Workspace", isInState(() -> LauncherState.NORMAL)); } @@ -245,7 +266,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { LauncherLayoutBuilder builder = new LauncherLayoutBuilder() .atHotseat(0).putApp("com.android.chrome", "com.google.android.apps.chrome.Main"); mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, builder); - clearLauncherData(); + reinitializeLauncherData(); final Workspace workspace = mLauncher.getWorkspace(); @@ -310,6 +331,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } @Test + @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/293191790 + @ScreenRecord @PortraitLandscape public void testWidgets() throws Exception { // Test opening widgets. @@ -371,6 +394,28 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } } + @Test + public void testLaunchHomeScreenMenuItem() { + // Drag the test app icon to home screen and open short cut menu from the icon + final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); + allApps.freeze(); + try { + allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false); + final AppIconMenu menu = mLauncher.getWorkspace().getWorkspaceAppIcon( + APP_NAME).openDeepShortcutMenu(); + + executeOnLauncher( + launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu", + isOptionsPopupVisible(launcher))); + + final AppIconMenuItem menuItem = menu.getMenuItem(1); + assertEquals("Wrong menu item", "Shortcut 2", menuItem.getText()); + menuItem.launch(getAppPackageName()); + } finally { + allApps.unfreeze(); + } + } + @PlatinumTest(focusArea = "launcher") @Test @PortraitLandscape @@ -503,19 +548,16 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { private void verifyAppUninstalledFromAllApps(Workspace workspace, String appName) { final HomeAllApps allApps = workspace.switchToAllApps(); - allApps.freeze(); - try { - assertNull(appName + " app was found on all apps after being uninstalled", - allApps.tryGetAppIcon(appName)); - } finally { - allApps.unfreeze(); - } + Wait.atMost(appName + " app was found on all apps after being uninstalled", + () -> allApps.tryGetAppIcon(appName) == null, + DEFAULT_UI_TIMEOUT, mLauncher); } - @Ignore("b/256615483") @Test @PortraitLandscape - @PlatinumTest(focusArea = "launcher") + // TODO(b/293944634): Remove Screenrecord after flaky debug, and add + // @PlatinumTest(focusArea = "launcher") back + @ScreenRecord public void testUninstallFromWorkspace() throws Exception { installDummyAppAndWaitForUIUpdate(); try { @@ -527,7 +569,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } @Test - @ScreenRecord // b/258071914 @PortraitLandscape @PlatinumTest(focusArea = "launcher") public void testUninstallFromAllApps() throws Exception { @@ -536,7 +577,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { Workspace workspace = mLauncher.getWorkspace(); final HomeAllApps allApps = workspace.switchToAllApps(); workspace = allApps.getAppIcon(DUMMY_APP_NAME).uninstall(); - waitForLauncherUIUpdate(); verifyAppUninstalledFromAllApps(workspace, DUMMY_APP_NAME); } finally { TestUtil.uninstallDummyApp(); @@ -560,7 +600,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { allApps.unfreeze(); } // Reset the workspace for the next shortcut creation. - initialize(this); + initialize(this, true); endTime = SystemClock.uptimeMillis(); elapsedTime = endTime - startTime; Log.d("testDragAppIconToWorkspaceCellTime", @@ -579,11 +619,19 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } } + /** + * Adds three icons to the workspace and removes one of them by dragging to uninstall. + */ @Test @ScreenRecord // b/241821721 @PlatinumTest(focusArea = "launcher") - public void getIconsPosition_afterIconRemoved_notContained() throws IOException { + public void uninstallWorkspaceIcon() throws IOException { Point[] gridPositions = getCornersAndCenterPositions(); + StringBuilder sb = new StringBuilder(); + for (Point p : gridPositions) { + sb.append(p).append(", "); + } + Log.d(ICON_MISSING, "allGridPositions: " + sb); createShortcutIfNotExist(STORE_APP_NAME, gridPositions[0]); createShortcutIfNotExist(MAPS_APP_NAME, gridPositions[1]); installDummyAppAndWaitForUIUpdate(); @@ -598,6 +646,10 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone( DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME); + // Debug for b/288944469 I want to test if we are not waiting enough after removing + // the icon to request the list of icons again, since the items are not removed + // immediately. This should reduce the flake rate + SystemClock.sleep(500); Map<String, Point> finalPositions = mLauncher.getWorkspace().getWorkspaceIconsPositions(); assertThat(finalPositions).doesNotContainKey(DUMMY_APP_NAME); @@ -677,8 +729,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); allApps.freeze(); try { - HomeAppIcon icon = allApps.getAppIcon(APP_NAME); - assertEquals("Wrong app icon name.", icon.getIconName(), APP_NAME); + // getAppIcon() already verifies that the icon is not null and is the correct icon name. + allApps.getAppIcon(APP_NAME); } finally { allApps.unfreeze(); } diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java index 083f58015c..4b2bade452 100644 --- a/tests/src/com/android/launcher3/ui/TestViewHelpers.java +++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java @@ -15,11 +15,14 @@ */ package com.android.launcher3.ui; -import static androidx.test.InstrumentationRegistry.getInstrumentation; -import static androidx.test.InstrumentationRegistry.getTargetContext; +import static android.os.Process.myUserHandle; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.launcher3.util.TestUtil.getOnUiThread; + +import android.app.Instrumentation; import android.content.ComponentName; -import android.os.Process; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -29,7 +32,6 @@ import com.android.launcher3.testcomponent.AppWidgetWithConfig; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.WidgetManagerHelper; -import java.util.concurrent.Callable; import java.util.function.Function; public class TestViewHelpers { @@ -38,23 +40,16 @@ public class TestViewHelpers { /** * Finds a widget provider which can fit on the home screen. * - * @param test test suite. * @param hasConfigureScreen if true, a provider with a config screen is returned. */ - public static LauncherAppWidgetProviderInfo findWidgetProvider(AbstractLauncherUiTest test, - final boolean hasConfigureScreen) { - LauncherAppWidgetProviderInfo info = - test.getOnUiThread(new Callable<LauncherAppWidgetProviderInfo>() { - @Override - public LauncherAppWidgetProviderInfo call() throws Exception { - ComponentName cn = new ComponentName(getInstrumentation().getContext(), - hasConfigureScreen ? AppWidgetWithConfig.class - : AppWidgetNoConfig.class); - Log.d(TAG, "findWidgetProvider componentName=" + cn.flattenToString()); - return new WidgetManagerHelper(getTargetContext()) - .findProvider(cn, Process.myUserHandle()); - } - }); + public static LauncherAppWidgetProviderInfo findWidgetProvider(boolean hasConfigureScreen) { + LauncherAppWidgetProviderInfo info = getOnUiThread(() -> { + Instrumentation i = getInstrumentation(); + ComponentName cn = new ComponentName(i.getContext(), + hasConfigureScreen ? AppWidgetWithConfig.class : AppWidgetNoConfig.class); + Log.d(TAG, "findWidgetProvider componentName=" + cn.flattenToString()); + return new WidgetManagerHelper(i.getTargetContext()).findProvider(cn, myUserHandle()); + }); if (info == null) { throw new IllegalArgumentException("No valid widget provider"); } diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java index 026766c7f4..5b9adcd80f 100644 --- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java +++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java @@ -18,7 +18,6 @@ package com.android.launcher3.ui; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST; -import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING; import static com.android.launcher3.util.TestUtil.installDummyAppForUser; import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; @@ -39,7 +38,6 @@ import com.android.launcher3.allapps.WorkEduCard; import com.android.launcher3.allapps.WorkPausedCard; import com.android.launcher3.allapps.WorkProfileManager; import com.android.launcher3.tapl.LauncherInstrumentation; -import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.TestUtil; import com.android.launcher3.util.rule.TestStabilityRule.Stability; @@ -78,8 +76,6 @@ public class WorkProfileTest extends AbstractLauncherUiTest { installDummyAppForUser(mProfileUserId); updateWorkProfileSetupSuccessful("am start-user", output); - Log.d(WORK_TAB_MISSING, "workProfileSuccessful? " + mWorkProfileSetupSuccessful + - " shellCmd: " + logStr); if (!mWorkProfileSetupSuccessful) { return; // no need to setup launcher since all tests will skip. } @@ -89,14 +85,13 @@ public class WorkProfileTest extends AbstractLauncherUiTest { waitForStateTransitionToEnd("Launcher internal state didn't switch to Normal", () -> NORMAL); waitForResumed("Launcher internal state is still Background"); - executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS)); + mLauncher.getWorkspace().switchToAllApps(); waitForStateTransitionToEnd("Launcher internal state didn't switch to All Apps", () -> ALL_APPS); } @After public void removeWorkProfile() throws Exception { - Log.d(TestProtocol.WORK_TAB_MISSING, "WorkProfileTest teardown"); executeOnLauncher(launcher -> { if (launcher == null || launcher.getAppsView() == null) { return; @@ -112,7 +107,6 @@ public class WorkProfileTest extends AbstractLauncherUiTest { mLauncher.getAllApps(); waitForLauncherCondition("Work tab not setup", launcher -> { if (launcher.getAppsView().getContentView() instanceof AllAppsPagedView) { - Log.d(WORK_TAB_MISSING, "Deferring AppsStore updates"); launcher.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST); return true; } diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java index e9a2b0ffb4..b2ce400d8d 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java @@ -30,10 +30,13 @@ import android.view.View; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; +import com.android.launcher3.Launcher; +import com.android.launcher3.celllayout.FavoriteItemsTransaction; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.testcomponent.WidgetConfigActivity; import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; import com.android.launcher3.ui.TestViewHelpers; import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.ShellCommandRule; @@ -63,7 +66,7 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { @Before public void setUp() throws Exception { super.setUp(); - mWidgetInfo = TestViewHelpers.findWidgetProvider(this, true /* hasConfigureScreen */); + mWidgetInfo = TestViewHelpers.findWidgetProvider(true /* hasConfigureScreen */); mAppWidgetManager = AppWidgetManager.getInstance(mTargetContext); } @@ -84,8 +87,7 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { * @param acceptConfig accept the config activity */ private void runTest(boolean acceptConfig) throws Throwable { - clearHomescreen(); - mDevice.pressHome(); + new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher); // Drag widget to homescreen WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor(); @@ -123,7 +125,10 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { @Override public boolean isTrue() throws Throwable { - return mMainThreadExecutor.submit(mActivityMonitor.itemExists(this)).get(); + return mMainThreadExecutor.submit(() -> { + Launcher l = Launcher.ACTIVITY_TRACKER.getCreatedActivity(); + return l != null && l.getWorkspace().getFirstMatch(this) != null; + }).get(); } @Override diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java index 3eb20e3820..9dca24bb28 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java @@ -18,17 +18,18 @@ package com.android.launcher3.ui.widget; import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import android.platform.test.annotations.PlatinumTest; +import android.platform.test.rule.ScreenRecordRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; -import com.android.launcher3.model.data.LauncherAppWidgetInfo; +import com.android.launcher3.celllayout.FavoriteItemsTransaction; import com.android.launcher3.tapl.Widget; import com.android.launcher3.tapl.WidgetResizeFrame; import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; import com.android.launcher3.ui.TestViewHelpers; import com.android.launcher3.util.rule.ShellCommandRule; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; @@ -51,14 +52,15 @@ public class AddWidgetTest extends AbstractLauncherUiTest { @PlatinumTest(focusArea = "launcher") @Test @PortraitLandscape + @ScreenRecordRule.ScreenRecord // b/289161193 public void testDragIcon() throws Throwable { - clearHomescreen(); - mDevice.pressHome(); + mLauncher.enableDebugTracing(); // b/289161193 + new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher); waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading()); final LauncherAppWidgetProviderInfo widgetInfo = - TestViewHelpers.findWidgetProvider(this, false /* hasConfigureScreen */); + TestViewHelpers.findWidgetProvider(false /* hasConfigureScreen */); WidgetResizeFrame resizeFrame = mLauncher .getWorkspace() @@ -66,11 +68,6 @@ public class AddWidgetTest extends AbstractLauncherUiTest { .getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager())) .dragWidgetToWorkspace(); - assertTrue(mActivityMonitor.itemExists( - (info, view) -> info instanceof LauncherAppWidgetInfo && - ((LauncherAppWidgetInfo) info).providerName.getClassName().equals( - widgetInfo.provider.getClassName())).call()); - assertNotNull("Widget resize frame not shown after widget add", resizeFrame); resizeFrame.dismiss(); @@ -78,6 +75,7 @@ public class AddWidgetTest extends AbstractLauncherUiTest { DEFAULT_UI_TIMEOUT); assertNotNull("Widget not found on the workspace", widget); widget.launch(getAppPackageName()); + mLauncher.disableDebugTracing(); // b/289161193 } /** @@ -90,8 +88,8 @@ public class AddWidgetTest extends AbstractLauncherUiTest { @Test @PortraitLandscape public void testDragCustomShortcut() throws Throwable { - clearHomescreen(); - mDevice.pressHome(); + new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher); + mLauncher.getWorkspace().openAllWidgets() .getWidget("com.android.launcher3.testcomponent.CustomShortcutConfigActivity") .dragToWorkspace(false, true); diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index 0f861eb1f3..7db31618b1 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -15,8 +15,13 @@ */ package com.android.launcher3.ui.widget; -import static androidx.test.InstrumentationRegistry.getTargetContext; - +import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; +import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; +import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_ID_NOT_VALID; +import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; +import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; +import static com.android.launcher3.provider.LauncherDbUtils.itemIdMatch; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.WidgetUtils.createWidgetInfo; import static org.junit.Assert.assertEquals; @@ -26,7 +31,6 @@ import static org.junit.Assert.assertTrue; import android.appwidget.AppWidgetManager; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; @@ -34,11 +38,14 @@ import android.database.Cursor; import android.os.Bundle; import android.widget.RemoteViews; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; +import com.android.launcher3.celllayout.FavoriteItemsTransaction; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.tapl.Widget; @@ -57,6 +64,7 @@ import org.junit.runner.RunWith; import java.util.HashSet; import java.util.Set; +import java.util.function.Consumer; /** * Tests for bind widget flow. @@ -70,24 +78,18 @@ public class BindWidgetTest extends AbstractLauncherUiTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); - private ContentResolver mResolver; - // Objects created during test, which should be cleaned up in the end. private Cursor mCursor; // App install session id. private int mSessionId = -1; + private LauncherModel mModel; + @Override @Before public void setUp() throws Exception { super.setUp(); - - mResolver = mTargetContext.getContentResolver(); - - // Clear all existing data - LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); - LauncherSettings.Settings.call(mResolver, - LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG); + mModel = LauncherAppState.getInstance(mTargetContext).getModel(); } @After @@ -103,34 +105,24 @@ public class BindWidgetTest extends AbstractLauncherUiTest { @Test public void testBindNormalWidget_withConfig() { - LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true); - LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true); - - addItemToScreen(item); + LauncherAppWidgetProviderInfo info = addWidgetToScreen(true, true, i -> { }); verifyWidgetPresent(info); } @Test public void testBindNormalWidget_withoutConfig() { - LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false); - LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true); - - addItemToScreen(item); + LauncherAppWidgetProviderInfo info = addWidgetToScreen(false, true, i -> { }); verifyWidgetPresent(info); } @Test public void testUnboundWidget_removed() { - LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false); - LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false); - item.appWidgetId = -33; - - addItemToScreen(item); + LauncherAppWidgetProviderInfo info = addWidgetToScreen(false, false, + item -> item.appWidgetId = -33); final Workspace workspace = mLauncher.getWorkspace(); // Item deleted from db - mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id), - null, null, null, null, null); + mCursor = queryItem(); assertEquals(0, mCursor.getCount()); // The view does not exist @@ -140,36 +132,26 @@ public class BindWidgetTest extends AbstractLauncherUiTest { @Test public void testPendingWidget_autoRestored() { // A non-restored widget with no config screen gets restored automatically. - LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false); - // Do not bind the widget - LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false); - item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID; - - addItemToScreen(item); + LauncherAppWidgetProviderInfo info = addWidgetToScreen(false, false, + item -> item.restoreStatus = FLAG_ID_NOT_VALID); verifyWidgetPresent(info); } @Test public void testPendingWidget_withConfigScreen() { // A non-restored widget with config screen get bound and shows a 'Click to setup' UI. - LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true); - // Do not bind the widget - LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false); - item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID; - - addItemToScreen(item); + LauncherAppWidgetProviderInfo info = addWidgetToScreen(true, false, + item -> item.restoreStatus = FLAG_ID_NOT_VALID); verifyPendingWidgetPresent(); - // Item deleted from db - mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id), - null, null, null, null, null); + mCursor = queryItem(); mCursor.moveToNext(); // Widget has a valid Id now. assertEquals(0, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED)) - & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); + & FLAG_ID_NOT_VALID); assertNotNull(AppWidgetManager.getInstance(mTargetContext) .getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex( LauncherSettings.Favorites.APPWIDGET_ID)))); @@ -185,7 +167,6 @@ public class BindWidgetTest extends AbstractLauncherUiTest { appWidgetManager.updateAppWidgetOptions(appWidgetId, b); appWidgetManager.updateAppWidget(appWidgetId, remoteViews); - // verify changes are reflected waitForLauncherCondition("App widget options did not update", l -> appWidgetManager.getAppWidgetOptions(appWidgetId).getBoolean( @@ -197,17 +178,12 @@ public class BindWidgetTest extends AbstractLauncherUiTest { @Test public void testPendingWidget_notRestored_removed() { - LauncherAppWidgetInfo item = getInvalidWidgetInfo(); - item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID - | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; - - addItemToScreen(item); + addPendingItemToScreen(getInvalidWidgetInfo(), FLAG_ID_NOT_VALID | FLAG_PROVIDER_NOT_READY); assertTrue("Pending widget exists", mLauncher.getWorkspace().tryGetPendingWidget(0) == null); // Item deleted from db - mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id), - null, null, null, null, null); + mCursor = queryItem(); assertEquals(0, mCursor.getCount()); } @@ -215,32 +191,25 @@ public class BindWidgetTest extends AbstractLauncherUiTest { public void testPendingWidget_notRestored_brokenInstall() { // A widget which is was being installed once, even if its not being // installed at the moment is not removed. - LauncherAppWidgetInfo item = getInvalidWidgetInfo(); - item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID - | LauncherAppWidgetInfo.FLAG_RESTORE_STARTED - | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; - - addItemToScreen(item); + addPendingItemToScreen(getInvalidWidgetInfo(), + FLAG_ID_NOT_VALID | FLAG_RESTORE_STARTED | FLAG_PROVIDER_NOT_READY); verifyPendingWidgetPresent(); // Verify item still exists in db - mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id), - null, null, null, null, null); + mCursor = queryItem(); assertEquals(1, mCursor.getCount()); // Widget still has an invalid id. mCursor.moveToNext(); - assertEquals(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID, + assertEquals(FLAG_ID_NOT_VALID, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED)) - & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); + & FLAG_ID_NOT_VALID); } @Test public void testPendingWidget_notRestored_activeInstall() throws Exception { // A widget which is being installed is not removed LauncherAppWidgetInfo item = getInvalidWidgetInfo(); - item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID - | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; // Create an active installer session SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL); @@ -248,19 +217,18 @@ public class BindWidgetTest extends AbstractLauncherUiTest { PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller(); mSessionId = installer.createSession(params); - addItemToScreen(item); + addPendingItemToScreen(item, FLAG_ID_NOT_VALID | FLAG_PROVIDER_NOT_READY); verifyPendingWidgetPresent(); // Verify item still exists in db - mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id), - null, null, null, null, null); + mCursor = queryItem(); assertEquals(1, mCursor.getCount()); // Widget still has an invalid id. mCursor.moveToNext(); - assertEquals(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID, + assertEquals(FLAG_ID_NOT_VALID, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED)) - & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); + & FLAG_ID_NOT_VALID); } private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) { @@ -275,6 +243,28 @@ public class BindWidgetTest extends AbstractLauncherUiTest { widget != null); } + private void addPendingItemToScreen(LauncherAppWidgetInfo item, int restoreStatus) { + item.restoreStatus = restoreStatus; + item.screenId = FIRST_SCREEN_ID; + new FavoriteItemsTransaction(mTargetContext) + .addItem(() -> item) + .commitAndLoadHome(mLauncher); + } + + private LauncherAppWidgetProviderInfo addWidgetToScreen(boolean hasConfigureScreen, + boolean bindWidget, Consumer<LauncherAppWidgetInfo> itemOverride) { + LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(hasConfigureScreen); + new FavoriteItemsTransaction(mTargetContext) + .addItem(() -> { + LauncherAppWidgetInfo item = createWidgetInfo(info, mTargetContext, bindWidget); + item.screenId = FIRST_SCREEN_ID; + itemOverride.accept(item); + return item; + }) + .commitAndLoadHome(mLauncher); + return info; + } + /** * Returns a LauncherAppWidgetInfo with package name which is not present on the device */ @@ -312,4 +302,14 @@ public class BindWidgetTest extends AbstractLauncherUiTest { item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP; return item; } + + private Cursor queryItem() { + try { + return MODEL_EXECUTOR.submit(() -> + mModel.getModelDbController().query( + TABLE_NAME, null, itemIdMatch(0), null, null)).get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java index bf9eb5acd6..a6b53692f6 100644 --- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java +++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java @@ -32,7 +32,9 @@ import android.view.View; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; +import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.celllayout.FavoriteItemsTransaction; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; @@ -134,8 +136,7 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher, Intent... commandIntents) throws Throwable { - clearHomescreen(); - mDevice.pressHome(); + new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher); // Open Pin item activity BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver( @@ -193,7 +194,10 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { @Override public boolean isTrue() throws Throwable { - return mMainThreadExecutor.submit(mActivityMonitor.itemExists(mOp)).get(); + return mMainThreadExecutor.submit(() -> { + Launcher l = Launcher.ACTIVITY_TRACKER.getCreatedActivity(); + return l != null && l.getWorkspace().getFirstMatch(mOp) != null; + }).get(); } } } diff --git a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java index 7ba0b53b22..8e5e9cc2a1 100644 --- a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java +++ b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java @@ -147,9 +147,8 @@ public class ThemeIconsTest extends AbstractLauncherUiTest { for (int i = parent.getChildCount() - 1; i >= 0; i--) { viewQueue.add(parent.getChildAt(i)); } - } else if (view instanceof BubbleTextView) { - BubbleTextView btv = (BubbleTextView) view; - if (title.equals(btv.getText())) { + } else if (view instanceof BubbleTextView btv) { + if (title.equals(btv.getContentDescription().toString())) { icon = btv; break; } diff --git a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java index c4b6d43466..62a8179a9d 100644 --- a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java +++ b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java @@ -29,6 +29,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.tapl.Workspace; import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; import com.android.launcher3.ui.TaplTestsLauncher3; import com.android.launcher3.util.LauncherLayoutBuilder; import com.android.launcher3.util.TestUtil; @@ -177,14 +178,15 @@ public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { public void testDragIconToPage3() { Workspace workspace = mLauncher.getWorkspace(); - workspace.dragIcon(workspace.getHotseatAppIcon("Phone"), 3); + // b/299522368 sometimes the phone app is not present in the hotseat. + workspace.dragIcon(workspace.getHotseatAppIcon("Chrome"), 3); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 2, 3); assertItemsOnPage(launcher, 0, "Play Store", "Maps"); assertPageEmpty(launcher, 1); assertPageEmpty(launcher, 2); - assertItemsOnPage(launcher, 3, "Phone"); + assertItemsOnPage(launcher, 3, "Chrome"); }); } diff --git a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt new file mode 100644 index 0000000000..8e4e99812e --- /dev/null +++ b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt @@ -0,0 +1,159 @@ +/* + * 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.launcher3.util + +import android.content.Context +import android.content.res.Configuration +import android.content.res.Resources +import android.graphics.Point +import android.graphics.Rect +import android.hardware.display.DisplayManager +import android.util.ArrayMap +import android.util.DisplayMetrics +import android.view.Display +import android.view.Surface +import androidx.test.annotation.UiThreadTest +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.launcher3.LauncherPrefs +import com.android.launcher3.util.DisplayController.CHANGE_DENSITY +import com.android.launcher3.util.DisplayController.CHANGE_ROTATION +import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener +import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext +import com.android.launcher3.util.window.CachedDisplayInfo +import com.android.launcher3.util.window.WindowManagerProxy +import kotlin.math.min +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.doNothing +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations +import org.mockito.stubbing.Answer + +/** Unit tests for {@link DisplayController} */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class DisplayControllerTest { + + private val appContext: Context = ApplicationProvider.getApplicationContext() + + @Mock private lateinit var context: SandboxContext + @Mock private lateinit var windowManagerProxy: WindowManagerProxy + @Mock private lateinit var launcherPrefs: LauncherPrefs + @Mock private lateinit var displayManager: DisplayManager + @Mock private lateinit var display: Display + @Mock private lateinit var resources: Resources + @Mock private lateinit var displayInfoChangeListener: DisplayInfoChangeListener + + private lateinit var displayController: DisplayController + + private val width = 2208 + private val height = 1840 + private val inset = 110 + private val densityDpi = 420 + private val density = densityDpi / DisplayMetrics.DENSITY_DEFAULT.toFloat() + private val bounds = + arrayOf( + WindowBounds(Rect(0, 0, width, height), Rect(0, inset, 0, 0), Surface.ROTATION_0), + WindowBounds(Rect(0, 0, height, width), Rect(0, inset, 0, 0), Surface.ROTATION_90), + WindowBounds(Rect(0, 0, width, height), Rect(0, inset, 0, 0), Surface.ROTATION_180), + WindowBounds(Rect(0, 0, height, width), Rect(0, inset, 0, 0), Surface.ROTATION_270) + ) + private val configuration = + Configuration(appContext.resources.configuration).apply { + densityDpi = this@DisplayControllerTest.densityDpi + screenWidthDp = (bounds[0].bounds.width() / density).toInt() + screenHeightDp = (bounds[0].bounds.height() / density).toInt() + smallestScreenWidthDp = min(screenWidthDp, screenHeightDp) + } + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + whenever(context.getObject(eq(WindowManagerProxy.INSTANCE))).thenReturn(windowManagerProxy) + whenever(context.getObject(eq(LauncherPrefs.INSTANCE))).thenReturn(launcherPrefs) + + // Mock WindowManagerProxy + val displayInfo = + CachedDisplayInfo(Point(width, height), Surface.ROTATION_0, Rect(0, 0, 0, 0)) + whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo) + whenever(windowManagerProxy.estimateInternalDisplayBounds(any())) + .thenAnswer( + Answer { + // Always create a new copy of bounds + val perDisplayBounds = ArrayMap<CachedDisplayInfo, List<WindowBounds>>() + perDisplayBounds[displayInfo] = bounds.toList() + return@Answer perDisplayBounds + } + ) + whenever(windowManagerProxy.getRealBounds(any(), any())).thenAnswer { i -> + bounds[i.getArgument<CachedDisplayInfo>(1).rotation] + } + + // Mock context + whenever(context.createWindowContext(any(), any(), nullable())).thenReturn(context) + whenever(context.getSystemService(eq(DisplayManager::class.java))) + .thenReturn(displayManager) + doNothing().`when`(context).registerComponentCallbacks(any()) + + // Mock display + whenever(display.rotation).thenReturn(displayInfo.rotation) + whenever(context.display).thenReturn(display) + whenever(displayManager.getDisplay(any())).thenReturn(display) + + // Mock resources + whenever(resources.configuration).thenReturn(configuration) + whenever(context.resources).thenReturn(resources) + + // Initialize DisplayController + displayController = DisplayController(context) + displayController.addChangeListener(displayInfoChangeListener) + } + + @Test + @UiThreadTest + fun testRotation() { + val displayInfo = + CachedDisplayInfo(Point(height, width), Surface.ROTATION_90, Rect(0, 0, 0, 0)) + whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo) + whenever(display.rotation).thenReturn(displayInfo.rotation) + val configuration = + Configuration(configuration).apply { + screenWidthDp = configuration.screenHeightDp + screenHeightDp = configuration.screenWidthDp + } + whenever(resources.configuration).thenReturn(configuration) + + displayController.onConfigurationChanged(configuration) + + verify(displayInfoChangeListener).onDisplayInfoChanged(any(), any(), eq(CHANGE_ROTATION)) + } + + @Test + @UiThreadTest + fun testFontScale() { + val configuration = Configuration(configuration).apply { fontScale = 1.2f } + whenever(resources.configuration).thenReturn(configuration) + + displayController.onConfigurationChanged(configuration) + + verify(displayInfoChangeListener).onDisplayInfoChanged(any(), any(), eq(CHANGE_DENSITY)) + } +} diff --git a/tests/src/com/android/launcher3/util/IconSizeStepsTest.kt b/tests/src/com/android/launcher3/util/IconSizeStepsTest.kt new file mode 100644 index 0000000000..d9e3377df9 --- /dev/null +++ b/tests/src/com/android/launcher3/util/IconSizeStepsTest.kt @@ -0,0 +1,67 @@ +/* + * 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.launcher3.util + +import android.content.Context +import android.content.res.Configuration +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.google.common.truth.Truth.assertThat +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class IconSizeStepsTest { + private var context: Context? = null + private val runningContext: Context = ApplicationProvider.getApplicationContext() + private lateinit var iconSizeSteps: IconSizeSteps + + @Before + fun setup() { + // 160dp makes 1px = 1dp + val config = + Configuration(runningContext.resources.configuration).apply { this.densityDpi = 160 } + context = runningContext.createConfigurationContext(config) + iconSizeSteps = IconSizeSteps(context!!.resources) + } + + @Test + fun minimumIconSize() { + assertThat(iconSizeSteps.minimumIconSize()).isEqualTo(52) + } + + @Test + fun getNextLowerIconSize() { + assertThat(iconSizeSteps.getNextLowerIconSize(66)).isEqualTo(63) + + // Assert that never goes below minimum + assertThat(iconSizeSteps.getNextLowerIconSize(52)).isEqualTo(52) + assertThat(iconSizeSteps.getNextLowerIconSize(30)).isEqualTo(52) + } + + @Test + fun getIconSmallerThan() { + assertThat(iconSizeSteps.getIconSmallerThan(60)).isEqualTo(59) + + // Assert that never goes below minimum + assertThat(iconSizeSteps.getIconSmallerThan(52)).isEqualTo(52) + assertThat(iconSizeSteps.getIconSmallerThan(30)).isEqualTo(52) + } +} diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java index 976afcdd13..261436b3ac 100644 --- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java +++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java @@ -15,34 +15,31 @@ */ package com.android.launcher3.util; -import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL; + import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static com.android.launcher3.LauncherSettings.Favorites.CONTENT_URI; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static com.android.launcher3.util.TestUtil.runOnExecutorSync; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import android.content.ComponentName; import android.content.ContentProvider; import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; -import android.content.res.Resources; -import android.database.sqlite.SQLiteDatabase; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Color; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor.AutoCloseOutputStream; -import android.os.Process; import android.provider.Settings; import android.test.mock.MockContentResolver; import android.util.ArrayMap; @@ -56,14 +53,10 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherModel.ModelUpdateTask; import com.android.launcher3.LauncherPrefs; -import com.android.launcher3.LauncherProvider; -import com.android.launcher3.LauncherSettings; import com.android.launcher3.model.AllAppsList; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.ItemInstallQueue; -import com.android.launcher3.model.data.AppInfo; -import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.pm.UserCache; import com.android.launcher3.testing.TestInformationProvider; @@ -72,37 +65,25 @@ import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext; import com.android.launcher3.util.window.WindowManagerProxy; import com.android.launcher3.widget.custom.CustomWidgetManager; -import org.mockito.ArgumentCaptor; - -import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; -import java.io.InputStreamReader; +import java.io.IOException; import java.io.OutputStreamWriter; -import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.List; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; -import java.util.function.Function; /** * Utility class to help manage Launcher Model and related objects for test. */ public class LauncherModelHelper { - public static final int DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP; - public static final int HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT; - - public static final int APP_ICON = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; - public static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; - public static final int NO__ICON = -1; - - public static final String TEST_PACKAGE = testContext().getPackageName(); + public static final String TEST_PACKAGE = getInstrumentation().getContext().getPackageName(); public static final String TEST_ACTIVITY = "com.android.launcher3.tests.Activity2"; + public static final String TEST_ACTIVITY2 = "com.android.launcher3.tests.Activity3"; + public static final String TEST_ACTIVITY3 = "com.android.launcher3.tests.Activity4"; // Authority for providing a test default-workspace-layout data. private static final String TEST_PROVIDER_AUTHORITY = @@ -110,39 +91,18 @@ public class LauncherModelHelper { private static final int DEFAULT_BITMAP_SIZE = 10; private static final int DEFAULT_GRID_SIZE = 4; - private final HashMap<Class, HashMap<String, Field>> mFieldCache = new HashMap<>(); - private final MockContentResolver mMockResolver = new MockContentResolver(); - public final TestLauncherProvider provider; public final SandboxModelContext sandboxContext; - public final long defaultProfileId; + private final RunnableList mDestroyTask = new RunnableList(); private BgDataModel mDataModel; - private AllAppsList mAllAppsList; public LauncherModelHelper() { - Context context = getApplicationContext(); - // System settings cache content provider. Ensure that they are statically initialized - Settings.Secure.getString(context.getContentResolver(), "test"); - Settings.System.getString(context.getContentResolver(), "test"); - Settings.Global.getString(context.getContentResolver(), "test"); - - provider = new TestLauncherProvider(); sandboxContext = new SandboxModelContext(); - defaultProfileId = UserCache.INSTANCE.get(sandboxContext) - .getSerialNumberForUser(Process.myUserHandle()); - setupProvider(LauncherProvider.AUTHORITY, provider); } public void setupProvider(String authority, ContentProvider provider) { - ProviderInfo providerInfo = new ProviderInfo(); - providerInfo.authority = authority; - providerInfo.applicationInfo = sandboxContext.getApplicationInfo(); - provider.attachInfo(sandboxContext, providerInfo); - mMockResolver.addProvider(providerInfo.authority, provider); - doReturn(providerInfo) - .when(sandboxContext.mPm) - .resolveContentProvider(eq(authority), anyInt()); + sandboxContext.setupProvider(authority, provider); } public LauncherModel getModel() { @@ -151,16 +111,35 @@ public class LauncherModelHelper { public synchronized BgDataModel getBgDataModel() { if (mDataModel == null) { - mDataModel = ReflectionHelpers.getField(getModel(), "mBgDataModel"); + getModel().enqueueModelUpdateTask(new ModelUpdateTask() { + @Override + public void init(@NonNull LauncherAppState app, @NonNull LauncherModel model, + @NonNull BgDataModel dataModel, @NonNull AllAppsList allAppsList, + @NonNull Executor uiExecutor) { + mDataModel = dataModel; + } + + @Override + public void run() { } + }); } return mDataModel; } - public synchronized AllAppsList getAllAppsList() { - if (mAllAppsList == null) { - mAllAppsList = ReflectionHelpers.getField(getModel(), "mBgAllAppsList"); - } - return mAllAppsList; + /** + * Creates a installer session for the provided package. + */ + public int createInstallerSession(String pkg) throws IOException { + SessionParams sp = new SessionParams(MODE_FULL_INSTALL); + sp.setAppPackageName(pkg); + Bitmap icon = Bitmap.createBitmap(100, 100, Config.ARGB_8888); + icon.eraseColor(Color.RED); + sp.setAppIcon(icon); + sp.setAppLabel(pkg); + PackageInstaller pi = sandboxContext.getPackageManager().getPackageInstaller(); + int sessionId = pi.createSession(sp); + mDestroyTask.add(() -> pi.abandonSession(sessionId)); + return sessionId; } public void destroy() { @@ -175,6 +154,8 @@ public class LauncherModelHelper { waitOrThrow(l1); sandboxContext.onDestroy(); l2.countDown(); + + mDestroyTask.executeAllAndDestroy(); } private void waitOrThrow(CountDownLatch latch) { @@ -186,184 +167,6 @@ public class LauncherModelHelper { } /** - * Synchronously executes the task and returns all the UI callbacks posted. - */ - public List<Runnable> executeTaskForTest(ModelUpdateTask task) throws Exception { - LauncherModel model = getModel(); - if (!model.isModelLoaded()) { - ReflectionHelpers.setField(model, "mModelLoaded", true); - } - Executor mockExecutor = mock(Executor.class); - model.enqueueModelUpdateTask(new ModelUpdateTask() { - @Override - public void init(@NonNull final LauncherAppState app, - @NonNull final LauncherModel model, @NonNull final BgDataModel dataModel, - @NonNull final AllAppsList allAppsList, @NonNull final Executor uiExecutor) { - task.init(app, model, dataModel, allAppsList, mockExecutor); - } - - @Override - public void run() { - task.run(); - } - }); - MODEL_EXECUTOR.submit(() -> null).get(); - - ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); - verify(mockExecutor, atLeast(0)).execute(captor.capture()); - return captor.getAllValues(); - } - - /** - * Synchronously executes a task on the model - */ - public <T> T executeSimpleTask(Function<BgDataModel, T> task) throws Exception { - BgDataModel dataModel = getBgDataModel(); - return MODEL_EXECUTOR.submit(() -> task.apply(dataModel)).get(); - } - - /** - * Initializes mock data for the test. - */ - public void initializeData(String resourceName) throws Exception { - BgDataModel bgDataModel = getBgDataModel(); - AllAppsList allAppsList = getAllAppsList(); - - MODEL_EXECUTOR.submit(() -> { - // Copy apk from resources to a local file and install from there. - Resources resources = testContext().getResources(); - int resId = resources.getIdentifier( - resourceName, "raw", testContext().getPackageName()); - try (BufferedReader reader = new BufferedReader(new InputStreamReader( - resources.openRawResource(resId)))) { - String line; - HashMap<String, Class> classMap = new HashMap<>(); - while ((line = reader.readLine()) != null) { - line = line.trim(); - if (line.startsWith("#") || line.isEmpty()) { - continue; - } - String[] commands = line.split(" "); - switch (commands[0]) { - case "classMap": - classMap.put(commands[1], Class.forName(commands[2])); - break; - case "bgItem": - bgDataModel.addItem(sandboxContext, - (ItemInfo) initItem(classMap.get(commands[1]), commands, 2), - false); - break; - case "allApps": - allAppsList.add((AppInfo) initItem(AppInfo.class, commands, 1), null); - break; - } - } - } catch (Exception e) { - throw new RuntimeException(e); - } - }).get(); - } - - private Object initItem(Class clazz, String[] fieldDef, int startIndex) throws Exception { - HashMap<String, Field> cache = mFieldCache.get(clazz); - if (cache == null) { - cache = new HashMap<>(); - Class c = clazz; - while (c != null) { - for (Field f : c.getDeclaredFields()) { - f.setAccessible(true); - cache.put(f.getName(), f); - } - c = c.getSuperclass(); - } - mFieldCache.put(clazz, cache); - } - - Object item = clazz.newInstance(); - for (int i = startIndex; i < fieldDef.length; i++) { - String[] fieldData = fieldDef[i].split("=", 2); - Field f = cache.get(fieldData[0]); - Class type = f.getType(); - if (type == int.class || type == long.class) { - f.set(item, Integer.parseInt(fieldData[1])); - } else if (type == CharSequence.class || type == String.class) { - f.set(item, fieldData[1]); - } else if (type == Intent.class) { - if (!fieldData[1].startsWith("#Intent")) { - fieldData[1] = "#Intent;" + fieldData[1] + ";end"; - } - f.set(item, Intent.parseUri(fieldData[1], 0)); - } else if (type == ComponentName.class) { - f.set(item, ComponentName.unflattenFromString(fieldData[1])); - } else { - throw new Exception("Added parsing logic for " - + f.getName() + " of type " + f.getType()); - } - } - return item; - } - - public int addItem(int type, int screen, int container, int x, int y) { - return addItem(type, screen, container, x, y, defaultProfileId, TEST_PACKAGE); - } - - public int addItem(int type, int screen, int container, int x, int y, long profileId) { - return addItem(type, screen, container, x, y, profileId, TEST_PACKAGE); - } - - public int addItem(int type, int screen, int container, int x, int y, String packageName) { - return addItem(type, screen, container, x, y, defaultProfileId, packageName); - } - - public int addItem(int type, int screen, int container, int x, int y, String packageName, - int id, Uri contentUri) { - addItem(type, screen, container, x, y, defaultProfileId, packageName, id, contentUri); - return id; - } - - /** - * Adds a mock item in the DB. - * @param type {@link #APP_ICON} or {@link #SHORTCUT} or >= 2 for - * folder (where the type represents the number of items in the folder). - */ - public int addItem(int type, int screen, int container, int x, int y, long profileId, - String packageName) { - int id = LauncherSettings.Settings.call(sandboxContext.getContentResolver(), - LauncherSettings.Settings.METHOD_NEW_ITEM_ID) - .getInt(LauncherSettings.Settings.EXTRA_VALUE); - addItem(type, screen, container, x, y, profileId, packageName, id, CONTENT_URI); - return id; - } - - public void addItem(int type, int screen, int container, int x, int y, long profileId, - String packageName, int id, Uri contentUri) { - ContentValues values = new ContentValues(); - values.put(LauncherSettings.Favorites._ID, id); - values.put(LauncherSettings.Favorites.CONTAINER, container); - values.put(LauncherSettings.Favorites.SCREEN, screen); - values.put(LauncherSettings.Favorites.CELLX, x); - values.put(LauncherSettings.Favorites.CELLY, y); - values.put(LauncherSettings.Favorites.SPANX, 1); - values.put(LauncherSettings.Favorites.SPANY, 1); - values.put(LauncherSettings.Favorites.PROFILE_ID, profileId); - - if (type == APP_ICON || type == SHORTCUT) { - values.put(LauncherSettings.Favorites.ITEM_TYPE, type); - values.put(LauncherSettings.Favorites.INTENT, - new Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0)); - } else { - values.put(LauncherSettings.Favorites.ITEM_TYPE, - LauncherSettings.Favorites.ITEM_TYPE_FOLDER); - // Add folder items. - for (int i = 0; i < type; i++) { - addItem(APP_ICON, 0, id, 0, 0, profileId); - } - } - - sandboxContext.getContentResolver().insert(contentUri, values); - } - - /** * Sets up a mock provider to load the provided layout by default, next time the layout loads */ public LauncherModelHelper setupDefaultLayoutProvider(LauncherLayoutBuilder builder) @@ -394,6 +197,9 @@ public class LauncherModelHelper { } }; setupProvider(TEST_PROVIDER_AUTHORITY, cp); + mDestroyTask.add(() -> runOnExecutorSync(MODEL_EXECUTOR, () -> + UiDevice.getInstance(getInstrumentation()).executeShellCommand( + "settings delete secure launcher3.layout.provider"))); return this; } @@ -402,46 +208,16 @@ public class LauncherModelHelper { */ public void loadModelSync() throws ExecutionException, InterruptedException { Callbacks mockCb = new Callbacks() { }; - Executors.MAIN_EXECUTOR.submit(() -> getModel().addCallbacksAndLoad(mockCb)).get(); + MAIN_EXECUTOR.submit(() -> getModel().addCallbacksAndLoad(mockCb)).get(); Executors.MODEL_EXECUTOR.submit(() -> { }).get(); - Executors.MAIN_EXECUTOR.submit(() -> { }).get(); - Executors.MAIN_EXECUTOR.submit(() -> getModel().removeCallbacks(mockCb)).get(); + MAIN_EXECUTOR.submit(() -> { }).get(); + MAIN_EXECUTOR.submit(() -> getModel().removeCallbacks(mockCb)).get(); } - /** - * An extension of LauncherProvider backed up by in-memory database. - */ - public static class TestLauncherProvider extends LauncherProvider { - - @Override - public boolean onCreate() { - return true; - } - - public SQLiteDatabase getDb() { - return getModelDbController().getDb(); - } - } - - public static boolean deleteContents(File dir) { - File[] files = dir.listFiles(); - boolean success = true; - if (files != null) { - for (File file : files) { - if (file.isDirectory()) { - success &= deleteContents(file); - } - if (!file.delete()) { - success = false; - } - } - } - return success; - } - - public class SandboxModelContext extends SandboxContext { + public static class SandboxModelContext extends SandboxContext { + private final MockContentResolver mMockResolver = new MockContentResolver(); private final ArrayMap<String, Object> mSpiedServices = new ArrayMap<>(); private final PackageManager mPm; private final File mDbDir; @@ -451,12 +227,30 @@ public class LauncherModelHelper { UserCache.INSTANCE, InstallSessionHelper.INSTANCE, LauncherPrefs.INSTANCE, LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE, DisplayController.INSTANCE, CustomWidgetManager.INSTANCE, - SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE, LockedUserState.INSTANCE, + SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE, + LockedUserState.INSTANCE, WallpaperColorHints.INSTANCE, ItemInstallQueue.INSTANCE, WindowManagerProxy.INSTANCE); + + // System settings cache content provider. Ensure that they are statically initialized + Settings.Secure.getString( + ApplicationProvider.getApplicationContext().getContentResolver(), "test"); + Settings.System.getString( + ApplicationProvider.getApplicationContext().getContentResolver(), "test"); + Settings.Global.getString( + ApplicationProvider.getApplicationContext().getContentResolver(), "test"); + mPm = spy(getBaseContext().getPackageManager()); mDbDir = new File(getCacheDir(), UUID.randomUUID().toString()); } + @Override + protected <T> T createObject(MainThreadInitializedObject<T> object) { + if (object == LauncherAppState.INSTANCE) { + return (T) new LauncherAppState(this, null /* iconCacheFileName */); + } + return super.createObject(object); + } + public SandboxModelContext allow(MainThreadInitializedObject object) { mAllowedObjects.add(object); return this; @@ -505,9 +299,30 @@ public class LauncherModelHelper { mSpiedServices.put(name, result); return result; } - } - private static Context testContext() { - return getInstrumentation().getContext(); + public void setupProvider(String authority, ContentProvider provider) { + ProviderInfo providerInfo = new ProviderInfo(); + providerInfo.authority = authority; + providerInfo.applicationInfo = getApplicationInfo(); + provider.attachInfo(this, providerInfo); + mMockResolver.addProvider(providerInfo.authority, provider); + doReturn(providerInfo).when(mPm).resolveContentProvider(eq(authority), anyInt()); + } + + private static boolean deleteContents(File dir) { + File[] files = dir.listFiles(); + boolean success = true; + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + success &= deleteContents(file); + } + if (!file.delete()) { + success = false; + } + } + } + return success; + } } } diff --git a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt index 84156e7101..92ab2cb90b 100644 --- a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt +++ b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt @@ -32,7 +32,7 @@ import org.mockito.Mockito.verifyZeroInteractions import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations -/** Unit tests for {@link LockedUserUtil} */ +/** Unit tests for {@link LockedUserState} */ @SmallTest @RunWith(AndroidJUnit4::class) class LockedUserStateTest { @@ -49,40 +49,31 @@ class LockedUserStateTest { @Test fun runOnUserUnlocked_runs_action_immediately_if_already_unlocked() { `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true) - LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context)) val action: Runnable = mock() - - LockedUserState.get(context).runOnUserUnlocked(action) + LockedUserState(context).runOnUserUnlocked(action) verify(action).run() } @Test fun runOnUserUnlocked_waits_to_run_action_until_user_is_unlocked() { `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false) - LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context)) val action: Runnable = mock() - - LockedUserState.get(context).runOnUserUnlocked(action) + val state = LockedUserState(context) + state.runOnUserUnlocked(action) verifyZeroInteractions(action) - - LockedUserState.get(context) - .mUserUnlockedReceiver - .onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED)) - + state.mUserUnlockedReceiver.onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED)) verify(action).run() } @Test fun isUserUnlocked_returns_true_when_user_is_unlocked() { `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true) - LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context)) - assertThat(LockedUserState.get(context).isUserUnlocked).isTrue() + assertThat(LockedUserState(context).isUserUnlocked).isTrue() } @Test fun isUserUnlocked_returns_false_when_user_is_locked() { `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false) - LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context)) - assertThat(LockedUserState.get(context).isUserUnlocked).isFalse() + assertThat(LockedUserState(context).isUserUnlocked).isFalse() } } diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java deleted file mode 100644 index ed2ec7b40a..0000000000 --- a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java +++ /dev/null @@ -1,500 +0,0 @@ -/* - * Copyright (C) 2018 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.launcher3.util; - -import static com.android.launcher3.util.Executors.createAndStartNewLooper; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.os.Handler; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; - -/** - * Event processor for reliably reproducing multithreaded apps race conditions in tests. - * - * The app notifies us about “events” that happen in its threads. The race condition test runs the - * test action multiple times (aka iterations), trying to generate all possible permutations of - * these events. It keeps a set of all seen event sequences and steers the execution towards - * executing events in previously unseen order. It does it by postponing execution of threads that - * would lead to an already seen sequence. - * - * If an event A occurs before event B in the sequence, this is how execution order looks like: - * Events: ... A ... B ... - * Events and instructions, guaranteed order: - * (instructions executed prior to A) A ... B (instructions executed after B) - * - * Each iteration has 3 parts (phases). - * Phase 1. Picking a previously seen event subsequence that we believe can have previously unseen - * continuations. Reproducing this sequence by pausing threads that would lead to other sequences. - * Phase 2. Trying to generate previously unseen continuation of the sequence from Phase 1. We need - * one new event after that sequence. All threads leading to seen continuations will be postponed - * for some short period of time. The phase ends once the new event is registered, or after the - * period of time ends (in which case we declare that the sequence can’t have new continuations). - * Phase 3. Releasing all threads and letting the test iteration run till its end. - * - * The iterations end when all seen paths have been declared “uncontinuable”. - * - * When we register event XXX:enter, we hold all other events until we register XXX:exit. - */ -public class RaceConditionReproducer { - private static final String TAG = "RaceConditionReproducer"; - - private static final boolean ENTER = true; - private static final boolean EXIT = false; - private static final String ENTER_POSTFIX = "enter"; - private static final String EXIT_POSTFIX = "exit"; - - private static final long SHORT_TIMEOUT_MS = 2000; - private static final long LONG_TIMEOUT_MS = 60000; - // Handler used to resume postponed events. - private static final Handler POSTPONED_EVENT_RESUME_HANDLER = - new Handler(createAndStartNewLooper("RaceConditionEventResumer")); - - public static String enterExitEvt(String eventName, boolean isEnter) { - return eventName + ":" + (isEnter ? ENTER_POSTFIX : EXIT_POSTFIX); - } - - public static String enterEvt(String eventName) { - return enterExitEvt(eventName, ENTER); - } - - public static String exitEvt(String eventName) { - return enterExitEvt(eventName, EXIT); - } - - /** - * Event in a particular sequence of events. A node in the prefix tree of all seen event - * sequences. - */ - private class EventNode { - // Events that were seen just after this event. - private final Map<String, EventNode> mNextEvents = new HashMap<>(); - // Whether we believe that further iterations will not be able to add more events to - // mNextEvents. - private boolean mStoppedAddingChildren = true; - - private void debugDump(StringBuilder sb, int indent, String name) { - for (int i = 0; i < indent; ++i) sb.append('.'); - sb.append(!mStoppedAddingChildren ? "+" : "-"); - sb.append(" : "); - sb.append(name); - if (mLastRegisteredEvent == this) sb.append(" <"); - sb.append('\n'); - - for (String key : mNextEvents.keySet()) { - mNextEvents.get(key).debugDump(sb, indent + 2, key); - } - } - - /** Number of leaves in the subtree with this node as a root. */ - private int numberOfLeafNodes() { - if (mNextEvents.isEmpty()) return 1; - - int leaves = 0; - for (String event : mNextEvents.keySet()) { - leaves += mNextEvents.get(event).numberOfLeafNodes(); - } - return leaves; - } - - /** - * Whether we believe that further iterations will not be able add nodes to the subtree with - * this node as a root. - */ - private boolean stoppedAddingChildrenToTree() { - if (!mStoppedAddingChildren) return false; - - for (String event : mNextEvents.keySet()) { - if (!mNextEvents.get(event).stoppedAddingChildrenToTree()) return false; - } - return true; - } - - /** - * In the subtree with this node as a root, tries finding a node where we may have a - * chance to add new children. - * If succeeds, returns true and fills 'path' with the sequence of events to that node; - * otherwise returns false. - */ - private boolean populatePathToGrowthPoint(List<String> path) { - for (String event : mNextEvents.keySet()) { - if (mNextEvents.get(event).populatePathToGrowthPoint(path)) { - path.add(0, event); - return true; - } - } - if (!mStoppedAddingChildren) { - // Mark that we have finished adding children. It will remain true if no new - // children are added, or will be set to false upon adding a new child. - mStoppedAddingChildren = true; - return true; - } - return false; - } - } - - // Starting point of all event sequences; the root of the prefix tree representation all - // sequences generated by test iterations. A test iteration can add nodes int it. - private EventNode mRoot = new EventNode(); - // During a test iteration, the last event that was registered. - private EventNode mLastRegisteredEvent; - // Length of the current sequence of registered events for the current test iteration. - private int mRegisteredEventCount = 0; - // During the first part of a test iteration, we go to a specific node under mRoot by - // 'playing back' mSequenceToFollow. During this part, all events that don't belong to this - // sequence get postponed. - private List<String> mSequenceToFollow = new ArrayList<>(); - // Collection of events that got postponed, with corresponding wait objects used to let them go. - private Map<String, Semaphore> mPostponedEvents = new HashMap<>(); - // Callback to run by POSTPONED_EVENT_RESUME_HANDLER, used to let go of all currently - // postponed events. - private Runnable mResumeAllEventsCallback; - // String representation of the sequence of events registered so far for the current test - // iteration. After registering any event, we output it to the log. The last output before - // the test failure can be later played back to reliable reproduce the exact sequence of - // events that broke the test. - // Format: EV1|EV2|...\EVN - private StringBuilder mCurrentSequence; - // When not null, we are in a repro mode. We run only one test iteration, and are trying to - // reproduce the event sequence represented by this string. The format is same as for - // mCurrentSequence. - private final String mReproString; - - /* Constructor for a normal test. */ - public RaceConditionReproducer() { - mReproString = null; - } - - /** - * Constructor for reliably reproducing a race condition failure. The developer should find in - * the log the latest "Repro sequence:" record and locally modify the test by passing that - * string to the constructor. Running the test will have only one iteration that will reliably - * "play back" that sequence. - */ - public RaceConditionReproducer(String reproString) { - mReproString = reproString; - } - - public RaceConditionReproducer(String... reproSequence) { - this(String.join("|", reproSequence)); - } - - public synchronized String getCurrentSequenceString() { - return mCurrentSequence.toString(); - } - - /** - * Starts a new test iteration. Events reported via RaceConditionTracker.onEvent before this - * call will be ignored. - */ - public synchronized void startIteration() { - mLastRegisteredEvent = mRoot; - mRegisteredEventCount = 0; - mCurrentSequence = new StringBuilder(); - Log.d(TAG, "Repro sequence: " + mCurrentSequence); - mSequenceToFollow = mReproString != null ? - parseReproString(mReproString) : generateSequenceToFollowLocked(); - Log.e(TAG, "---- Start of iteration; state:\n" + dumpStateLocked()); - checkIfCompletedSequenceToFollowLocked(); - - TraceHelperForTest.setRaceConditionReproducer(this); - } - - /** - * Ends a new test iteration. Events reported via RaceConditionTracker.onEvent after this call - * will be ignored. - * Returns whether we need more iterations. - */ - public synchronized boolean finishIteration() { - TraceHelperForTest.setRaceConditionReproducer(null); - - runResumeAllEventsCallbackLocked(); - assertTrue("Non-empty postponed events", mPostponedEvents.isEmpty()); - assertTrue("Last registered event is :enter", lastEventAsEnter() == null); - - // No events came after mLastRegisteredEvent. It doesn't make sense to come to it again - // because we won't see new continuations. - mLastRegisteredEvent.mStoppedAddingChildren = true; - Log.e(TAG, "---- End of iteration; state:\n" + dumpStateLocked()); - if (mReproString != null) { - assertTrue("Repro mode: failed to reproduce the sequence", - mCurrentSequence.toString().startsWith(mReproString)); - } - // If we are in a repro mode, we need only one iteration. Otherwise, continue if the tree - // has prospective growth points. - return mReproString == null && !mRoot.stoppedAddingChildrenToTree(); - } - - private static List<String> parseReproString(String reproString) { - return Arrays.asList(reproString.split("\\|")); - } - - /** - * Called when the app issues an event. - */ - public void onEvent(String event) { - final Semaphore waitObject = tryRegisterEvent(event); - if (waitObject != null) { - waitUntilCanRegister(event, waitObject); - } - } - - /** - * Returns whether the last event was not an XXX:enter, or this event is a matching XXX:exit. - */ - private boolean canRegisterEventNowLocked(String event) { - final String lastEventAsEnter = lastEventAsEnter(); - final String thisEventAsExit = eventAsExit(event); - - if (lastEventAsEnter != null) { - if (!lastEventAsEnter.equals(thisEventAsExit)) { - assertTrue("YYY:exit after XXX:enter", thisEventAsExit == null); - // Last event was :enter, but this event is not :exit. - return false; - } - } else { - // Previous event was not :enter. - assertTrue(":exit after a non-enter event", thisEventAsExit == null); - } - return true; - } - - /** - * Registers an event issued by the app and returns null or decides that the event must be - * postponed, and returns an object to wait on. - */ - private synchronized Semaphore tryRegisterEvent(String event) { - Log.d(TAG, "Event issued by the app: " + event); - - if (!canRegisterEventNowLocked(event)) { - return createWaitObjectForPostponedEventLocked(event); - } - - if (mRegisteredEventCount < mSequenceToFollow.size()) { - // We are in the first part of the iteration. We only register events that follow the - // mSequenceToFollow and postponing all other events. - if (event.equals(mSequenceToFollow.get(mRegisteredEventCount))) { - // The event is the next one expected in the sequence. Register it. - registerEventLocked(event); - - // If there are postponed events that could continue the sequence, register them. - while (mRegisteredEventCount < mSequenceToFollow.size() && - mPostponedEvents.containsKey( - mSequenceToFollow.get(mRegisteredEventCount))) { - registerPostponedEventLocked(mSequenceToFollow.get(mRegisteredEventCount)); - } - - // Perhaps we just completed the required sequence... - checkIfCompletedSequenceToFollowLocked(); - } else { - // The event is not the next one in the sequence. Postpone it. - return createWaitObjectForPostponedEventLocked(event); - } - } else if (mRegisteredEventCount == mSequenceToFollow.size()) { - // The second phase of the iteration. We have just registered the whole - // mSequenceToFollow, and want to add previously not seen continuations for the last - // node in the sequence aka 'growth point'. - if (!mLastRegisteredEvent.mNextEvents.containsKey(event) || mReproString != null) { - // The event was never seen as a continuation for the current node. - // Or we are in repro mode, in which case we are not in business of generating - // new sequences after we've played back the required sequence. - // Register it immediately. - registerEventLocked(event); - } else { - // The event was seen as a continuation for the current node. Postpone it, hoping - // that a new event will come from other threads. - return createWaitObjectForPostponedEventLocked(event); - } - } else { - // The third phase of the iteration. We are past the growth point and register - // everything that comes. - registerEventLocked(event); - // Register events that may have been postponed while waiting for an :exit event - // during the third phase. We don't do this if just registered event is :enter. - if (eventAsEnter(event) == null && mRegisteredEventCount > mSequenceToFollow.size()) { - registerPostponedEventsLocked(new HashSet<>(mPostponedEvents.keySet())); - } - } - return null; - } - - /** Called when there are chances that we just have registered the whole mSequenceToFollow. */ - private void checkIfCompletedSequenceToFollowLocked() { - if (mRegisteredEventCount == mSequenceToFollow.size()) { - // We just entered the second phase of the iteration. We have just registered the - // whole mSequenceToFollow, and want to add previously not seen continuations for the - // last node in the sequence aka 'growth point'. All seen continuations will be - // postponed for SHORT_TIMEOUT_MS. At the end of this time period, we'll let them go. - scheduleResumeAllEventsLocked(); - - // Among the events that were postponed during the first stage, there may be an event - // that wasn't seen after the current. If so, register it immediately because this - // creates a new sequence. - final Set<String> keys = new HashSet<>(mPostponedEvents.keySet()); - keys.removeAll(mLastRegisteredEvent.mNextEvents.keySet()); - if (!keys.isEmpty()) { - registerPostponedEventLocked(keys.iterator().next()); - } - } - } - - private Semaphore createWaitObjectForPostponedEventLocked(String event) { - final Semaphore waitObject = new Semaphore(0); - assertTrue("Event already postponed: " + event, !mPostponedEvents.containsKey(event)); - mPostponedEvents.put(event, waitObject); - return waitObject; - } - - private void waitUntilCanRegister(String event, Semaphore waitObject) { - try { - assertTrue("Never registered event: " + event, - waitObject.tryAcquire(LONG_TIMEOUT_MS, TimeUnit.MILLISECONDS)); - } catch (InterruptedException e) { - fail("Wait was interrupted"); - } - } - - /** Schedules resuming all postponed events after SHORT_TIMEOUT_MS */ - private void scheduleResumeAllEventsLocked() { - assertTrue(mResumeAllEventsCallback == null); - mResumeAllEventsCallback = this::allEventsResumeCallback; - POSTPONED_EVENT_RESUME_HANDLER.postDelayed(mResumeAllEventsCallback, SHORT_TIMEOUT_MS); - } - - private synchronized void allEventsResumeCallback() { - assertTrue("In callback, but callback is not set", mResumeAllEventsCallback != null); - mResumeAllEventsCallback = null; - registerPostponedEventsLocked(new HashSet<>(mPostponedEvents.keySet())); - } - - private void registerPostponedEventsLocked(Collection<String> events) { - for (String event : events) { - registerPostponedEventLocked(event); - if (eventAsEnter(event) != null) { - // Once :enter is registered, switch to waiting for :exit to come. Won't register - // other postponed events. - break; - } - } - } - - private void registerPostponedEventLocked(String event) { - mPostponedEvents.remove(event).release(); - registerEventLocked(event); - } - - /** - * If the last registered event was XXX:enter, returns XXX, otherwise, null. - */ - private String lastEventAsEnter() { - return eventAsEnter(mCurrentSequence.substring(mCurrentSequence.lastIndexOf("|") + 1)); - } - - /** - * If the event is XXX:postfix, returns XXX, otherwise, null. - */ - private static String prefixFromPostfixedEvent(String event, String postfix) { - final int columnPos = event.indexOf(':'); - if (columnPos != -1 && postfix.equals(event.substring(columnPos + 1))) { - return event.substring(0, columnPos); - } - return null; - } - - /** - * If the event is XXX:enter, returns XXX, otherwise, null. - */ - private static String eventAsEnter(String event) { - return prefixFromPostfixedEvent(event, ENTER_POSTFIX); - } - - /** - * If the event is XXX:exit, returns XXX, otherwise, null. - */ - private static String eventAsExit(String event) { - return prefixFromPostfixedEvent(event, EXIT_POSTFIX); - } - - private void registerEventLocked(String event) { - assertTrue(canRegisterEventNowLocked(event)); - - Log.d(TAG, "Actually registering event: " + event); - EventNode next = mLastRegisteredEvent.mNextEvents.get(event); - if (next == null) { - // This event wasn't seen after mLastRegisteredEvent. - next = new EventNode(); - mLastRegisteredEvent.mNextEvents.put(event, next); - // The fact that we've added a new event after the previous one means that the - // previous event is still a growth point, unless this event is :exit, which means - // that the previous event is :enter. - mLastRegisteredEvent.mStoppedAddingChildren = eventAsExit(event) != null; - } - - mLastRegisteredEvent = next; - mRegisteredEventCount++; - - if (mCurrentSequence.length() > 0) mCurrentSequence.append("|"); - mCurrentSequence.append(event); - Log.d(TAG, "Repro sequence: " + mCurrentSequence); - } - - private void runResumeAllEventsCallbackLocked() { - if (mResumeAllEventsCallback != null) { - POSTPONED_EVENT_RESUME_HANDLER.removeCallbacks(mResumeAllEventsCallback); - mResumeAllEventsCallback.run(); - } - } - - private CharSequence dumpStateLocked() { - StringBuilder sb = new StringBuilder(); - - sb.append("Sequence to follow: "); - for (String event : mSequenceToFollow) sb.append(" " + event); - sb.append(".\n"); - sb.append("Registered event count: " + mRegisteredEventCount); - - sb.append("\nPostponed events: "); - for (String event : mPostponedEvents.keySet()) sb.append(" " + event); - sb.append("."); - - sb.append("\nNodes: \n"); - mRoot.debugDump(sb, 0, ""); - return sb; - } - - public int numberOfLeafNodes() { - return mRoot.numberOfLeafNodes(); - } - - private List<String> generateSequenceToFollowLocked() { - ArrayList<String> sequence = new ArrayList<>(); - mRoot.populatePathToGrowthPoint(sequence); - return sequence; - } -} diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java b/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java deleted file mode 100644 index 59f21734f4..0000000000 --- a/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2018 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.launcher3.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; - -@LargeTest -@RunWith(AndroidJUnit4.class) -public class RaceConditionReproducerTest { - private final static String SOME_VALID_SEQUENCE_3_3 = "B1|A1|A2|B2|A3|B3"; - - private static int factorial(int n) { - int res = 1; - for (int i = 2; i <= n; ++i) res *= i; - return res; - } - - RaceConditionReproducer eventProcessor; - - @Before - public void setup() { - eventProcessor = new RaceConditionReproducer(); - } - - @After - public void tearDown() { - TraceHelperForTest.cleanup(); - } - - private void run3_3_TestAction() throws InterruptedException { - Thread tb = new Thread(() -> { - eventProcessor.onEvent("B1"); - eventProcessor.onEvent("B2"); - eventProcessor.onEvent("B3"); - }); - tb.start(); - - eventProcessor.onEvent("A1"); - eventProcessor.onEvent("A2"); - eventProcessor.onEvent("A3"); - - tb.join(); - } - - @Test - @Ignore // The test is too long for continuous testing. - // 2 threads, 3 events each. - public void test3_3() throws Exception { - boolean sawTheValidSequence = false; - - for (; ; ) { - eventProcessor.startIteration(); - run3_3_TestAction(); - final boolean needMoreIterations = eventProcessor.finishIteration(); - - sawTheValidSequence = sawTheValidSequence || - SOME_VALID_SEQUENCE_3_3.equals(eventProcessor.getCurrentSequenceString()); - - if (!needMoreIterations) break; - } - - assertEquals("Wrong number of leaf nodes", - factorial(3 + 3) / (factorial(3) * factorial(3)), - eventProcessor.numberOfLeafNodes()); - assertTrue(sawTheValidSequence); - } - - @Test - @Ignore // The test is too long for continuous testing. - // 2 threads, 3 events, including enter-exit pairs each. - public void test3_3_enter_exit() throws Exception { - boolean sawTheValidSequence = false; - - for (; ; ) { - eventProcessor.startIteration(); - Thread tb = new Thread(() -> { - eventProcessor.onEvent("B1:enter"); - eventProcessor.onEvent("B1:exit"); - eventProcessor.onEvent("B2"); - eventProcessor.onEvent("B3:enter"); - eventProcessor.onEvent("B3:exit"); - }); - tb.start(); - - eventProcessor.onEvent("A1"); - eventProcessor.onEvent("A2:enter"); - eventProcessor.onEvent("A2:exit"); - eventProcessor.onEvent("A3:enter"); - eventProcessor.onEvent("A3:exit"); - - tb.join(); - final boolean needMoreIterations = eventProcessor.finishIteration(); - - sawTheValidSequence = sawTheValidSequence || - "B1:enter|B1:exit|A1|A2:enter|A2:exit|B2|A3:enter|A3:exit|B3:enter|B3:exit". - equals(eventProcessor.getCurrentSequenceString()); - - if (!needMoreIterations) break; - } - - assertEquals("Wrong number of leaf nodes", - factorial(3 + 3) / (factorial(3) * factorial(3)), - eventProcessor.numberOfLeafNodes()); - assertTrue(sawTheValidSequence); - } - - @Test - // 2 threads, 3 events each; reproducing a particular event sequence. - public void test3_3_ReproMode() throws Exception { - eventProcessor = new RaceConditionReproducer(SOME_VALID_SEQUENCE_3_3); - eventProcessor.startIteration(); - run3_3_TestAction(); - assertTrue(!eventProcessor.finishIteration()); - assertEquals(SOME_VALID_SEQUENCE_3_3, eventProcessor.getCurrentSequenceString()); - - assertEquals("Wrong number of leaf nodes", 1, eventProcessor.numberOfLeafNodes()); - } - - @Test - @Ignore // The test is too long for continuous testing. - // 2 threads with 2 events; 1 thread with 1 event. - public void test2_1_2() throws Exception { - for (; ; ) { - eventProcessor.startIteration(); - Thread tb = new Thread(() -> { - eventProcessor.onEvent("B1"); - eventProcessor.onEvent("B2"); - }); - tb.start(); - - Thread tc = new Thread(() -> { - eventProcessor.onEvent("C1"); - }); - tc.start(); - - eventProcessor.onEvent("A1"); - eventProcessor.onEvent("A2"); - - tb.join(); - tc.join(); - - if (!eventProcessor.finishIteration()) break; - } - - assertEquals("Wrong number of leaf nodes", - factorial(2 + 2 + 1) / (factorial(2) * factorial(2) * factorial(1)), - eventProcessor.numberOfLeafNodes()); - } - - @Test - @Ignore // The test is too long for continuous testing. - // 2 threads with 2 events; 1 thread with 1 event. Includes enter-exit pairs. - public void test2_1_2_enter_exit() throws Exception { - for (; ; ) { - eventProcessor.startIteration(); - Thread tb = new Thread(() -> { - eventProcessor.onEvent("B1:enter"); - eventProcessor.onEvent("B1:exit"); - eventProcessor.onEvent("B2:enter"); - eventProcessor.onEvent("B2:exit"); - }); - tb.start(); - - Thread tc = new Thread(() -> { - eventProcessor.onEvent("C1:enter"); - eventProcessor.onEvent("C1:exit"); - }); - tc.start(); - - eventProcessor.onEvent("A1:enter"); - eventProcessor.onEvent("A1:exit"); - eventProcessor.onEvent("A2:enter"); - eventProcessor.onEvent("A2:exit"); - - tb.join(); - tc.join(); - - if (!eventProcessor.finishIteration()) break; - } - - assertEquals("Wrong number of leaf nodes", - factorial(2 + 2 + 1) / (factorial(2) * factorial(2) * factorial(1)), - eventProcessor.numberOfLeafNodes()); - } -} diff --git a/tests/src/com/android/launcher3/util/TestResourceHelper.kt b/tests/src/com/android/launcher3/util/TestResourceHelper.kt index fb03fe1b53..cf80ece740 100644 --- a/tests/src/com/android/launcher3/util/TestResourceHelper.kt +++ b/tests/src/com/android/launcher3/util/TestResourceHelper.kt @@ -23,12 +23,18 @@ import com.android.launcher3.R import com.android.launcher3.tests.R as TestR import kotlin.IntArray -class TestResourceHelper(private val context: Context, private val specsFileId: Int) : +class TestResourceHelper(private val context: Context, specsFileId: Int) : ResourceHelper(context, specsFileId) { override fun obtainStyledAttributes(attrs: AttributeSet, styleId: IntArray): TypedArray { - var clone = styleId.clone() - if (styleId == R.styleable.SpecSize) clone = TestR.styleable.SpecSize - else if (styleId == R.styleable.WorkspaceSpec) clone = TestR.styleable.WorkspaceSpec + val clone = + when { + styleId.contentEquals(R.styleable.SizeSpec) -> TestR.styleable.SizeSpec + styleId.contentEquals(R.styleable.WorkspaceSpec) -> TestR.styleable.WorkspaceSpec + styleId.contentEquals(R.styleable.FolderSpec) -> TestR.styleable.FolderSpec + styleId.contentEquals(R.styleable.AllAppsSpec) -> TestR.styleable.AllAppsSpec + else -> styleId.clone() + } + return context.obtainStyledAttributes(attrs, clone) } } diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java index f8cd995aab..21059e6075 100644 --- a/tests/src/com/android/launcher3/util/TestUtil.java +++ b/tests/src/com/android/launcher3/util/TestUtil.java @@ -18,14 +18,13 @@ package com.android.launcher3.util; import static android.util.Base64.NO_PADDING; import static android.util.Base64.NO_WRAP; -import static androidx.test.InstrumentationRegistry.getContext; -import static androidx.test.InstrumentationRegistry.getInstrumentation; -import static androidx.test.InstrumentationRegistry.getTargetContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY; import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL; import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG; +import android.app.Instrumentation; import android.app.blob.BlobHandle; import android.app.blob.BlobStoreManager; import android.content.Context; @@ -35,9 +34,12 @@ import android.os.AsyncTask; import android.os.Handler; import android.os.Looper; import android.os.ParcelFileDescriptor.AutoCloseOutputStream; +import android.os.Process; import android.os.UserHandle; import android.provider.Settings; +import android.system.OsConstants; import android.util.Base64; +import android.util.Log; import androidx.test.uiautomator.UiDevice; @@ -52,26 +54,36 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.MessageDigest; +import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Predicate; import java.util.function.ToIntFunction; public class TestUtil { + private static final String TAG = "TestUtil"; + public static final String DUMMY_PACKAGE = "com.example.android.aardwolf"; - public static final int DEFAULT_USER_ID = 0; + public static final String DUMMY_CLASS_NAME = "com.example.android.aardwolf.Activity1"; + public static final long DEFAULT_UI_TIMEOUT = 10000; public static void installDummyApp() throws IOException { - installDummyAppForUser(DEFAULT_USER_ID); + final int defaultUserId = getMainUserId(); + installDummyAppForUser(defaultUserId); } public static void installDummyAppForUser(int userId) throws IOException { + Instrumentation instrumentation = getInstrumentation(); // Copy apk from resources to a local file and install from there. - final Resources resources = getContext().getResources(); + final Resources resources = instrumentation.getContext().getResources(); final InputStream in = resources.openRawResource( resources.getIdentifier("aardwolf_dummy_app", - "raw", getContext().getPackageName())); - final String apkFilename = getInstrumentation().getTargetContext(). - getFilesDir().getPath() + "/dummy_app.apk"; + "raw", instrumentation.getContext().getPackageName())); + final String apkFilename = instrumentation.getTargetContext() + .getFilesDir().getPath() + "/dummy_app.apk"; try (PackageInstallCheck pic = new PackageInstallCheck()) { final FileOutputStream out = new FileOutputStream(apkFilename); @@ -84,7 +96,7 @@ public class TestUtil { in.close(); out.close(); - final String result = UiDevice.getInstance(getInstrumentation()) + final String result = UiDevice.getInstance(instrumentation) .executeShellCommand("pm install --user " + userId + " " + apkFilename); Assert.assertTrue( "Failed to install wellbeing test apk; make sure the device is rooted", @@ -96,6 +108,23 @@ public class TestUtil { } /** + * Returns the main user ID. NOTE: For headless system it is NOT 0. Returns 0 by default, if + * there is no main user. + * + * @return a main user ID + */ + public static int getMainUserId() throws IOException { + Instrumentation instrumentation = getInstrumentation(); + final String result = UiDevice.getInstance(instrumentation) + .executeShellCommand("cmd user get-main-user"); + try { + return Integer.parseInt(result.trim()); + } catch (NumberFormatException e) { + return 0; + } + } + + /** * Utility class to override a boolean flag during test. Note that the returned SafeCloseable * must be closed to restore the original state */ @@ -159,6 +188,50 @@ public class TestUtil { Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, null); } + /** + * Utility method to run a task synchronously which converts any exceptions to RuntimeException + */ + public static void runOnExecutorSync(ExecutorService executor, UncheckedRunnable task) { + try { + executor.submit(() -> { + try { + task.run(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }).get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Runs the callback on the UI thread and returns the result. + */ + public static <T> T getOnUiThread(final Callable<T> callback) { + try { + FutureTask<T> task = new FutureTask<>(callback); + if (Looper.myLooper() == Looper.getMainLooper()) { + task.run(); + } else { + new Handler(Looper.getMainLooper()).post(task); + } + return task.get(DEFAULT_UI_TIMEOUT, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + Log.e(TAG, "Timeout in getOnUiThread, sending SIGABRT", e); + Process.sendSignal(Process.myPid(), OsConstants.SIGABRT); + throw new RuntimeException(e); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + /** Interface to indicate a runnable which can throw any exception. */ + public interface UncheckedRunnable { + /** Method to run the task */ + void run() throws Exception; + } + private static class PackageInstallCheck extends LauncherApps.Callback implements AutoCloseable { @@ -166,7 +239,8 @@ public class TestUtil { final LauncherApps mLauncherApps; PackageInstallCheck() { - mLauncherApps = getTargetContext().getSystemService(LauncherApps.class); + mLauncherApps = getInstrumentation().getTargetContext() + .getSystemService(LauncherApps.class); mLauncherApps.registerCallback(this, new Handler(Looper.getMainLooper())); } diff --git a/tests/src/com/android/launcher3/util/TraceHelperForTest.java b/tests/src/com/android/launcher3/util/TraceHelperForTest.java deleted file mode 100644 index f1c8a67f9a..0000000000 --- a/tests/src/com/android/launcher3/util/TraceHelperForTest.java +++ /dev/null @@ -1,116 +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 com.android.launcher3.util; - -import java.util.LinkedList; -import java.util.function.IntConsumer; - -public class TraceHelperForTest extends TraceHelper { - - private static final TraceHelperForTest INSTANCE_FOR_TEST = new TraceHelperForTest(); - - private final ThreadLocal<LinkedList<TraceInfo>> mStack = - ThreadLocal.withInitial(LinkedList::new); - - private RaceConditionReproducer mRaceConditionReproducer; - private IntConsumer mFlagsChangeListener; - - public static void setRaceConditionReproducer(RaceConditionReproducer reproducer) { - TraceHelper.INSTANCE = INSTANCE_FOR_TEST; - INSTANCE_FOR_TEST.mRaceConditionReproducer = reproducer; - } - - public static void cleanup() { - INSTANCE_FOR_TEST.mRaceConditionReproducer = null; - INSTANCE_FOR_TEST.mFlagsChangeListener = null; - } - - public static void setFlagsChangeListener(IntConsumer listener) { - TraceHelper.INSTANCE = INSTANCE_FOR_TEST; - INSTANCE_FOR_TEST.mFlagsChangeListener = listener; - } - - private TraceHelperForTest() { } - - @Override - public Object beginSection(String sectionName, int flags) { - LinkedList<TraceInfo> stack = mStack.get(); - TraceInfo info = new TraceInfo(sectionName, flags); - stack.add(info); - - if ((flags & TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS) != 0 - && mRaceConditionReproducer != null) { - mRaceConditionReproducer.onEvent(RaceConditionReproducer.enterEvt(sectionName)); - } - updateBinderTracking(stack); - - super.beginSection(sectionName, flags); - return info; - } - - @Override - public void endSection(Object token) { - LinkedList<TraceInfo> stack = mStack.get(); - if (stack.size() == 0) { - new Throwable().printStackTrace(); - } - TraceInfo info = (TraceInfo) token; - stack.remove(info); - if ((info.flags & TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS) != 0 - && mRaceConditionReproducer != null) { - mRaceConditionReproducer.onEvent(RaceConditionReproducer.exitEvt(info.sectionName)); - } - updateBinderTracking(stack); - - super.endSection(token); - } - - @Override - public Object beginFlagsOverride(int flags) { - LinkedList<TraceInfo> stack = mStack.get(); - TraceInfo info = new TraceInfo(null, flags); - stack.add(info); - updateBinderTracking(stack); - super.beginFlagsOverride(flags); - return info; - } - - @Override - public void endFlagsOverride(Object token) { - super.endFlagsOverride(token); - LinkedList<TraceInfo> stack = mStack.get(); - TraceInfo info = (TraceInfo) token; - stack.remove(info); - updateBinderTracking(stack); - } - - private void updateBinderTracking(LinkedList<TraceInfo> stack) { - if (mFlagsChangeListener != null) { - mFlagsChangeListener.accept(stack.stream() - .mapToInt(info -> info.flags).reduce(0, (a, b) -> a | b)); - } - } - - private static class TraceInfo { - public final String sectionName; - public final int flags; - - TraceInfo(String sectionName, int flags) { - this.sectionName = sectionName; - this.flags = flags; - } - } -} diff --git a/tests/src/com/android/launcher3/util/WidgetUtils.java b/tests/src/com/android/launcher3/util/WidgetUtils.java index b0df0558dd..027a31aeb2 100644 --- a/tests/src/com/android/launcher3/util/WidgetUtils.java +++ b/tests/src/com/android/launcher3/util/WidgetUtils.java @@ -18,18 +18,14 @@ package com.android.launcher3.util; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; - import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.os.Bundle; import android.os.Process; import com.android.launcher3.LauncherSettings; -import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.LauncherWidgetHolder; @@ -88,33 +84,6 @@ public class WidgetUtils { } /** - * Adds {@param item} on the homescreen on the 0th screen - */ - public static void addItemToScreen(ItemInfo item, Context targetContext) { - ContentResolver resolver = targetContext.getContentResolver(); - int screenId = FIRST_SCREEN_ID; - // Update the screen id counter for the provider. - LauncherSettings.Settings.call(resolver, - LauncherSettings.Settings.METHOD_NEW_SCREEN_ID); - - if (screenId > FIRST_SCREEN_ID) { - screenId = FIRST_SCREEN_ID; - } - - // Insert the item - ContentWriter writer = new ContentWriter(targetContext); - item.id = LauncherSettings.Settings.call( - resolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID) - .getInt(LauncherSettings.Settings.EXTRA_VALUE); - item.screenId = screenId; - item.onAddToDatabase(writer); - writer.put(LauncherSettings.Favorites._ID, item.id); - resolver.insert(LauncherSettings.Favorites.CONTENT_URI, - writer.getValues(targetContext)); - } - - - /** * Creates a {@link AppWidgetProviderInfo} for the provided component name */ public static AppWidgetProviderInfo createAppWidgetProviderInfo(ComponentName cn) { diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java index 7ca6a06ed2..62d70ad1cd 100644 --- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java +++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java @@ -8,10 +8,9 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.test.core.app.ApplicationProvider; import androidx.test.uiautomator.UiDevice; -import com.android.app.viewcapture.ViewCapture; +import com.android.app.viewcapture.data.ExportedData; import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.ui.AbstractLauncherUiTest; @@ -24,22 +23,21 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.util.function.Supplier; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class FailureWatcher extends TestWatcher { private static final String TAG = "FailureWatcher"; private static boolean sSavedBugreport = false; - final private UiDevice mDevice; private final LauncherInstrumentation mLauncher; @NonNull - private final ViewCapture mViewCapture; + private final Supplier<ExportedData> mViewCaptureDataSupplier; - public FailureWatcher(UiDevice device, LauncherInstrumentation launcher, - @NonNull ViewCapture viewCapture) { - mDevice = device; + public FailureWatcher(LauncherInstrumentation launcher, + @NonNull Supplier<ExportedData> viewCaptureDataSupplier) { mLauncher = launcher; - mViewCapture = viewCapture; + mViewCaptureDataSupplier = viewCaptureDataSupplier; } @Override @@ -71,7 +69,7 @@ public class FailureWatcher extends TestWatcher { @Override protected void failed(Throwable e, Description description) { - onError(mLauncher, description, e, mViewCapture); + onError(mLauncher, description, e, mViewCaptureDataSupplier); } static File diagFile(Description description, String prefix, String ext) { @@ -86,7 +84,7 @@ public class FailureWatcher extends TestWatcher { } private static void onError(LauncherInstrumentation launcher, Description description, - Throwable e, @Nullable ViewCapture viewCapture) { + Throwable e, @Nullable Supplier<ExportedData> viewCaptureDataSupplier) { final File sceenshot = diagFile(description, "TestScreenshot", "png"); final File hierarchy = diagFile(description, "Hierarchy", "zip"); @@ -103,9 +101,10 @@ public class FailureWatcher extends TestWatcher { dumpCommand("cmd window dump-visible-window-views", out); out.closeEntry(); - if (viewCapture != null) { + if (viewCaptureDataSupplier != null) { out.putNextEntry(new ZipEntry("FS/data/misc/wmtrace/failed_test.vc")); - viewCapture.dumpTo(out, ApplicationProvider.getApplicationContext()); + final ExportedData exportedData = viewCaptureDataSupplier.get(); + if (exportedData != null) exportedData.writeTo(out); out.closeEntry(); } } catch (Exception ignored) { diff --git a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java deleted file mode 100644 index e9a52f84df..0000000000 --- a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2017 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.launcher3.util.rule; - -import android.app.Activity; - -import com.android.launcher3.Launcher; -import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator; - -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import java.util.concurrent.Callable; - -/** - * Test rule to get the current Launcher activity. - */ -public class LauncherActivityRule extends SimpleActivityRule<Launcher> { - - public LauncherActivityRule() { - super(Launcher.class); - } - - @Override - public Statement apply(Statement base, Description description) { - - return new MyStatement(base) { - @Override - public void onActivityStarted(Activity activity) { - if (activity instanceof Launcher) { - ((Launcher) activity).getRotationHelper().forceAllowRotationForTesting(true); - } - } - }; - } - - public Callable<Boolean> itemExists(final ItemOperator op) { - return () -> { - Launcher launcher = getActivity(); - if (launcher == null) { - return false; - } - return launcher.getWorkspace().getFirstMatch(op) != null; - }; - } -}
\ No newline at end of file diff --git a/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java b/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java deleted file mode 100644 index 2eedec30b6..0000000000 --- a/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java +++ /dev/null @@ -1,105 +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 com.android.launcher3.util.rule; - -import android.app.Activity; -import android.app.Application; -import android.app.Application.ActivityLifecycleCallbacks; -import android.os.Bundle; - -import androidx.test.InstrumentationRegistry; - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -/** - * Test rule to get the current activity. - */ -public class SimpleActivityRule<T extends Activity> implements TestRule { - - private final Class<T> mClass; - private T mActivity; - - public SimpleActivityRule(Class<T> clazz) { - mClass = clazz; - } - - @Override - public Statement apply(Statement base, Description description) { - return new MyStatement(base); - } - - public T getActivity() { - return mActivity; - } - - protected class MyStatement extends Statement implements ActivityLifecycleCallbacks { - - private final Statement mBase; - - public MyStatement(Statement base) { - mBase = base; - } - - @Override - public void evaluate() throws Throwable { - Application app = (Application) - InstrumentationRegistry.getTargetContext().getApplicationContext(); - app.registerActivityLifecycleCallbacks(this); - try { - mBase.evaluate(); - } finally { - app.unregisterActivityLifecycleCallbacks(this); - mActivity = null; - } - } - - @Override - public void onActivityCreated(Activity activity, Bundle bundle) { - if (activity != null && mClass.isInstance(activity)) { - mActivity = (T) activity; - } - } - - @Override - public void onActivityStarted(Activity activity) { - } - - @Override - public void onActivityResumed(Activity activity) { - } - - @Override - public void onActivityPaused(Activity activity) { - } - - @Override - public void onActivityStopped(Activity activity) { - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { - } - - @Override - public void onActivityDestroyed(Activity activity) { - if (activity == mActivity) { - mActivity = null; - } - } - } -}
\ No newline at end of file diff --git a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java index f33a50ae5a..38de07192a 100644 --- a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java +++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java @@ -17,6 +17,8 @@ package com.android.launcher3.util.rule; import static androidx.test.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assume.assumeTrue; + import android.content.pm.PackageManager; import android.os.Build; import android.util.Log; @@ -69,12 +71,9 @@ public class TestStabilityRule implements TestRule { return new Statement() { @Override public void evaluate() throws Throwable { - if ((stability.flavors() & getRunFlavor()) != 0) { - Log.d(TAG, "Running " + description.getDisplayName()); - base.evaluate(); - } else { - Log.d(TAG, "Skipping " + description.getDisplayName()); - } + assumeTrue("Ignoring the test due to @Stability annotation", + (stability.flavors() & getRunFlavor()) != 0); + base.evaluate(); } }; } else { diff --git a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt index 0c6553998d..ccbae4fb0c 100644 --- a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt +++ b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt @@ -20,9 +20,21 @@ import android.app.Application import android.media.permission.SafeCloseable import android.os.Bundle import androidx.test.core.app.ApplicationProvider +import androidx.test.platform.app.InstrumentationRegistry import com.android.app.viewcapture.SimpleViewCapture import com.android.app.viewcapture.ViewCapture.MAIN_EXECUTOR +import com.android.app.viewcapture.data.ExportedData +import com.android.launcher3.tapl.TestHelpers import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter +import com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT +import com.android.launcher3.util.viewcapture_analysis.ViewCaptureAnalyzer +import java.io.BufferedOutputStream +import java.io.FileOutputStream +import java.io.IOException +import java.io.OutputStreamWriter +import java.util.function.Supplier +import org.junit.Assert.assertTrue +import org.junit.Assert.fail import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement @@ -33,28 +45,33 @@ import org.junit.runners.model.Statement * * This rule will not work in OOP tests that don't have access to the activity under test. */ -class ViewCaptureRule : TestRule { - val viewCapture = SimpleViewCapture("test-view-capture") +class ViewCaptureRule(var alreadyOpenActivitySupplier: Supplier<Activity?>) : TestRule { + private val viewCapture = SimpleViewCapture("test-view-capture") + var viewCaptureData: ExportedData? = null + private set override fun apply(base: Statement, description: Description): Statement { + // Skip view capture collection in Launcher3 tests to avoid hidden API check exception. + if ( + "com.android.launcher3.tests" == + InstrumentationRegistry.getInstrumentation().context.packageName + ) + return base + return object : Statement() { override fun evaluate() { + viewCaptureData = null val windowListenerCloseables = mutableListOf<SafeCloseable>() + startCapturingExistingActivity(windowListenerCloseables) + val lifecycleCallbacks = object : ActivityLifecycleCallbacksAdapter { override fun onActivityCreated(activity: Activity, bundle: Bundle?) { - super.onActivityCreated(activity, bundle) - windowListenerCloseables.add( - viewCapture.startCapture( - activity.window.decorView, - "${description.testClass?.simpleName}.${description.methodName}" - ) - ) + startCapture(windowListenerCloseables, activity) } override fun onActivityDestroyed(activity: Activity) { - super.onActivityDestroyed(activity) viewCapture.stopCapture(activity.window.decorView) } } @@ -67,6 +84,9 @@ class ViewCaptureRule : TestRule { } finally { application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks) + viewCaptureData = + viewCapture.getExportedData(ApplicationProvider.getApplicationContext()) + // Clean up ViewCapture references here rather than in onActivityDestroyed so // test code can access view hierarchy capture. onActivityDestroyed would delete // view capture data before FailureWatcher could output it as a test artifact. @@ -74,7 +94,69 @@ class ViewCaptureRule : TestRule { // is removed while onDraw is running, resulting in an IllegalStateException. MAIN_EXECUTOR.execute { windowListenerCloseables.onEach(SafeCloseable::close) } } + + analyzeViewCapture(description) } + + private fun startCapturingExistingActivity( + windowListenerCloseables: MutableCollection<SafeCloseable> + ) { + val alreadyOpenActivity = alreadyOpenActivitySupplier.get() + if (alreadyOpenActivity != null) { + startCapture(windowListenerCloseables, alreadyOpenActivity) + } + } + + private fun startCapture( + windowListenerCloseables: MutableCollection<SafeCloseable>, + activity: Activity + ) { + windowListenerCloseables.add( + viewCapture.startCapture( + activity.window.decorView, + "${description.testClass?.simpleName}.${description.methodName}" + ) + ) + } + } + } + + private fun analyzeViewCapture(description: Description) { + // OOP tests don't produce ViewCapture data + if (!TestHelpers.isInLauncherProcess()) return + + // Due to flakiness of ViewCapture verifier, don't run the check in presubmit + if (TestStabilityRule.getRunFlavor() != PLATFORM_POSTSUBMIT) return + + var frameCount = 0 + for (i in 0 until viewCaptureData!!.windowDataCount) { + frameCount += viewCaptureData!!.getWindowData(i).frameDataCount + } + assertTrue("Empty ViewCapture data", frameCount > 0) + + val anomalies: Map<String, String> = ViewCaptureAnalyzer.getAnomalies(viewCaptureData) + if (!anomalies.isEmpty()) { + val diagFile = FailureWatcher.diagFile(description, "ViewAnomalies", "txt") + try { + OutputStreamWriter(BufferedOutputStream(FileOutputStream(diagFile))).use { writer -> + writer.write("View animation anomalies detected.\r\n") + writer.write( + "To suppress an anomaly for a view, add its full path to the PATHS_TO_IGNORE list in the corresponding AnomalyDetector.\r\n" + ) + writer.write("List of views with animation anomalies:\r\n") + + for ((viewPath, message) in anomalies) { + writer.write("View: $viewPath\r\n $message\r\n") + } + } + } catch (ex: IOException) { + throw RuntimeException(ex) + } + + val (viewPath, message) = anomalies.entries.first() + fail( + "${anomalies.size} view(s) had animation anomalies during the test, including view: $viewPath: $message\r\nSee ${diagFile.name} for details." + ) } } } diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java new file mode 100644 index 0000000000..4b65439db5 --- /dev/null +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java @@ -0,0 +1,205 @@ +/* + * 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.launcher3.util.viewcapture_analysis; + +import com.android.launcher3.util.viewcapture_analysis.ViewCaptureAnalyzer.AnalysisNode; + +import java.util.List; + +/** + * Anomaly detector that triggers an error when alpha of a view changes too rapidly. + * Invisible views are treated as if they had zero alpha. + */ +final class AlphaJumpDetector extends AnomalyDetector { + // Commonly used parts of the paths to ignore. + private static final String CONTENT = "DecorView|LinearLayout|FrameLayout:id/content|"; + private static final String DRAG_LAYER = + CONTENT + "LauncherRootView:id/launcher|DragLayer:id/drag_layer|"; + private static final String RECENTS_DRAG_LAYER = + CONTENT + "LauncherRootView:id/launcher|RecentsDragLayer:id/drag_layer|"; + + private static final IgnoreNode IGNORED_NODES_ROOT = buildIgnoreNodesTree(List.of( + CONTENT + + "AddItemDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + + "/add_item_bottom_sheet|LinearLayout:id/add_item_bottom_sheet_content" + + "|ScrollView:id/widget_preview_scroll_view|WidgetCell:id/widget_cell" + + "|WidgetCellPreview:id/widget_preview_container|ImageView:id/widget_badge", + CONTENT + + "AddItemDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + + "/add_item_bottom_sheet|LinearLayout:id/add_item_bottom_sheet_content" + + "|ScrollView:id/widget_preview_scroll_view|WidgetCell:id/widget_cell" + + "|WidgetCellPreview:id/widget_preview_container|WidgetCell$1|FrameLayout" + + "|ImageView:id/icon", + CONTENT + "AddItemDragLayer:id/add_item_drag_layer|View", + DRAG_LAYER + + "AppWidgetResizeFrame|FrameLayout|ImageButton:id/widget_reconfigure_button", + DRAG_LAYER + + "AppWidgetResizeFrame|FrameLayout|ImageView:id/widget_resize_bottom_handle", + DRAG_LAYER + "AppWidgetResizeFrame|FrameLayout|ImageView:id/widget_resize_frame", + DRAG_LAYER + "AppWidgetResizeFrame|FrameLayout|ImageView:id/widget_resize_left_handle", + DRAG_LAYER + "AppWidgetResizeFrame|FrameLayout|ImageView:id/widget_resize_right_handle", + DRAG_LAYER + "AppWidgetResizeFrame|FrameLayout|ImageView:id/widget_resize_top_handle", + DRAG_LAYER + "FloatingTaskView|FloatingTaskThumbnailView:id/thumbnail", + DRAG_LAYER + "FloatingTaskView|SplitPlaceholderView:id/split_placeholder", + DRAG_LAYER + "Folder|FolderPagedView:id/folder_content", + DRAG_LAYER + "LauncherAllAppsContainerView:id/apps_view", + DRAG_LAYER + "LauncherDragView", + DRAG_LAYER + "LauncherRecentsView:id/overview_panel", + DRAG_LAYER + + "NexusOverviewActionsView:id/overview_actions_view|FrameLayout:id" + + "/select_mode_buttons|ImageButton:id/close", + DRAG_LAYER + + "NexusOverviewActionsView:id/overview_actions_view|LinearLayout:id" + + "/action_buttons|Button:id/action_screenshot", + DRAG_LAYER + + "NexusOverviewActionsView:id/overview_actions_view|LinearLayout:id" + + "/action_buttons|Button:id/action_select", + DRAG_LAYER + + "NexusOverviewActionsView:id/overview_actions_view|LinearLayout:id" + + "/action_buttons|Button:id/action_split", + DRAG_LAYER + + "NexusOverviewActionsView:id/overview_actions_view|LinearLayout:id" + + "/action_buttons|Space:id/action_split_space", + DRAG_LAYER + + "PopupContainerWithArrow:id/popup_container|LinearLayout:id" + + "/deep_shortcuts_container|DeepShortcutView:id/deep_shortcut_material" + + "|DeepShortcutTextView:id/bubble_text", + DRAG_LAYER + + "PopupContainerWithArrow:id/popup_container|LinearLayout:id" + + "/deep_shortcuts_container|DeepShortcutView:id/deep_shortcut_material|View" + + ":id/icon", + DRAG_LAYER + + "PopupContainerWithArrow:id/popup_container|LinearLayout:id" + + "/system_shortcuts_container|DeepShortcutView:id/system_shortcut" + + "|BubbleTextView:id/bubble_text", + DRAG_LAYER + + "PopupContainerWithArrow:id/popup_container|LinearLayout:id" + + "/system_shortcuts_container|DeepShortcutView:id/system_shortcut|View:id" + + "/icon", + DRAG_LAYER + + "PopupContainerWithArrow:id/popup_container|LinearLayout:id" + + "/system_shortcuts_container|ImageView", + DRAG_LAYER + + "PopupContainerWithArrow:id/popup_container|LinearLayout:id" + + "/widget_shortcut_container|DeepShortcutView:id/system_shortcut" + + "|BubbleTextView:id/bubble_text", + DRAG_LAYER + + "PopupContainerWithArrow:id/popup_container|LinearLayout:id" + + "/widget_shortcut_container|DeepShortcutView:id/system_shortcut|View:id/icon", + DRAG_LAYER + "SearchContainerView:id/apps_view", + DRAG_LAYER + "Snackbar|TextView:id/action", + DRAG_LAYER + "Snackbar|TextView:id/label", + DRAG_LAYER + "SplitInstructionsView|AppCompatTextView:id/split_instructions_text", + DRAG_LAYER + "TaskMenuView|LinearLayout:id/menu_option_layout", + DRAG_LAYER + "TaskMenuViewWithArrow|LinearLayout:id/menu_option_layout", + DRAG_LAYER + "TaskMenuView|TextView:id/task_name", + DRAG_LAYER + "View", + DRAG_LAYER + "WidgetsFullSheet|SpringRelativeLayout:id/container", + DRAG_LAYER + "WidgetsTwoPaneSheet|SpringRelativeLayout:id/container", + CONTENT + "LauncherRootView:id/launcher|FloatingIconView", + RECENTS_DRAG_LAYER + "ArrowTipView", + DRAG_LAYER + "ArrowTipView", + DRAG_LAYER + "FallbackRecentsView:id/overview_panel", + RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel", + DRAG_LAYER + + "NexusOverviewActionsView:id/overview_actions_view" + + "|LinearLayout:id/action_buttons|Button:id/action_screenshot", + RECENTS_DRAG_LAYER + + "NexusOverviewActionsView:id/overview_actions_view" + + "|LinearLayout:id/action_buttons|Button:id/action_screenshot", + DRAG_LAYER + + "NexusOverviewActionsView:id/overview_actions_view" + + "|LinearLayout:id/action_buttons|Button:id/action_select", + RECENTS_DRAG_LAYER + + "NexusOverviewActionsView:id/overview_actions_view" + + "|LinearLayout:id/action_buttons|Button:id/action_select", + DRAG_LAYER + + "NexusOverviewActionsView:id/overview_actions_view" + + "|LinearLayout:id/action_buttons|Button:id/action_split", + RECENTS_DRAG_LAYER + + "NexusOverviewActionsView:id/overview_actions_view" + + "|LinearLayout:id/action_buttons|Button:id/action_split", + DRAG_LAYER + "IconView" + )); + + // Minimal increase or decrease of view's alpha between frames that triggers the error. + private static final float ALPHA_JUMP_THRESHOLD = 1f; + + // Per-AnalysisNode data that's specific to this detector. + private static class NodeData { + public boolean ignoreAlphaJumps; + + // If ignoreNode is null, then this AnalysisNode node will be ignored if its parent is + // ignored. + // Otherwise, this AnalysisNode will be ignored if ignoreNode is a leaf i.e. has no + // children. + public IgnoreNode ignoreNode; + } + + private NodeData getNodeData(AnalysisNode info) { + return (NodeData) info.detectorsData[detectorOrdinal]; + } + + @Override + void initializeNode(AnalysisNode info) { + final NodeData nodeData = new NodeData(); + info.detectorsData[detectorOrdinal] = nodeData; + + // If the parent view ignores alpha jumps, its descendants will too. + final boolean parentIgnoresAlphaJumps = info.parent != null && getNodeData( + info.parent).ignoreAlphaJumps; + if (parentIgnoresAlphaJumps) { + nodeData.ignoreAlphaJumps = true; + return; + } + + // Parent view doesn't ignore alpha jumps. + // Initialize this AnalysisNode's ignore-node with the corresponding child of the + // ignore-node of the parent, if present. + final IgnoreNode parentIgnoreNode = info.parent != null + ? getNodeData(info.parent).ignoreNode + : IGNORED_NODES_ROOT; + nodeData.ignoreNode = parentIgnoreNode != null + ? parentIgnoreNode.children.get(info.nodeIdentity) : null; + // AnalysisNode will be ignored if the corresponding ignore-node is a leaf. + nodeData.ignoreAlphaJumps = + nodeData.ignoreNode != null && nodeData.ignoreNode.children.isEmpty(); + } + + @Override + String detectAnomalies(AnalysisNode oldInfo, AnalysisNode newInfo, int frameN, long timestamp, + int windowSizePx) { + // If the view was previously seen, proceed with analysis only if it was present in the + // view hierarchy in the previous frame. + if (oldInfo != null && oldInfo.frameN != frameN) return null; + + final AnalysisNode latestInfo = newInfo != null ? newInfo : oldInfo; + final NodeData nodeData = getNodeData(latestInfo); + if (nodeData.ignoreAlphaJumps) return null; + + final float oldAlpha = oldInfo != null ? oldInfo.alpha : 0; + final float newAlpha = newInfo != null ? newInfo.alpha : 0; + final float alphaDeltaAbs = Math.abs(newAlpha - oldAlpha); + + if (alphaDeltaAbs >= ALPHA_JUMP_THRESHOLD) { + nodeData.ignoreAlphaJumps = true; // No need to report alpha jump in children. + return String.format( + "Alpha jump detected: alpha change: %s (%s -> %s), threshold: %s", + alphaDeltaAbs, oldAlpha, newAlpha, ALPHA_JUMP_THRESHOLD); + } + return null; + } +} diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/AnomalyDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/AnomalyDetector.java new file mode 100644 index 0000000000..786791c22f --- /dev/null +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/AnomalyDetector.java @@ -0,0 +1,85 @@ +/* + * 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.launcher3.util.viewcapture_analysis; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** + * Detector of one kind of anomaly. + */ +abstract class AnomalyDetector { + // Index of this detector in ViewCaptureAnalyzer.ANOMALY_DETECTORS + public int detectorOrdinal; + + /** + * Element of the tree of ignored nodes. + * If the "children" map is empty, then this node should be ignored, i.e. the analysis shouldn't + * run for it. + * I.e. ignored nodes correspond to the leaves in the ignored nodes tree. + */ + protected static class IgnoreNode { + // Map from child node identities to ignore-nodes for these children. + public final Map<String, IgnoreNode> children = new HashMap<>(); + } + + // Converts the list of full paths of nodes to ignore to a more efficient tree of ignore-nodes. + protected static IgnoreNode buildIgnoreNodesTree(Iterable<String> pathsToIgnore) { + final IgnoreNode root = new IgnoreNode(); + for (String pathToIgnore : pathsToIgnore) { + // Scan the diag path of an ignored node and add its elements into the tree. + IgnoreNode currentIgnoreNode = root; + for (String part : pathToIgnore.split("\\|")) { + // Ensure that the child of the node is added to the tree. + IgnoreNode child = currentIgnoreNode.children.get(part); + if (child == null) { + currentIgnoreNode.children.put(part, child = new IgnoreNode()); + } + currentIgnoreNode = child; + } + } + return root; + } + + /** + * Initializes fields of the node that are specific to the anomaly detected by this + * detector. + */ + abstract void initializeNode(@NonNull ViewCaptureAnalyzer.AnalysisNode info); + + /** + * Detects anomalies by looking at the last occurrence of a view, and the current one. + * null value means that the view. 'oldInfo' and 'newInfo' cannot be both null. + * If an anomaly is detected, an exception will be thrown. + * + * @param oldInfo the view, as seen in the last frame that contained it in the view + * hierarchy before 'currentFrame'. 'null' means that the view is first seen + * in the 'currentFrame'. + * @param newInfo the view in the view hierarchy of the 'currentFrame'. 'null' means that + * the view is not present in the 'currentFrame', but was present in the + * previous frame. + * @param frameN number of the current frame. + * @param windowSizePx maximum of the window width and height, in pixels. + * @return Anomaly diagnostic message if an anomaly has been detected; null otherwise. + */ + abstract String detectAnomalies( + @Nullable ViewCaptureAnalyzer.AnalysisNode oldInfo, + @Nullable ViewCaptureAnalyzer.AnalysisNode newInfo, int frameN, + long frameTimeNs, int windowSizePx); +} diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java new file mode 100644 index 0000000000..8b88ace181 --- /dev/null +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java @@ -0,0 +1,176 @@ +/* + * 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.launcher3.util.viewcapture_analysis; + +import static org.junit.Assert.assertTrue; + +import com.android.launcher3.util.viewcapture_analysis.ViewCaptureAnalyzer.AnalysisNode; + +import java.util.List; + +/** + * Anomaly detector that triggers an error when a view flashes, i.e. appears or disappears for a too + * short period of time. + */ +final class FlashDetector extends AnomalyDetector { + // Maximum time period of a view visibility or invisibility that is recognized as a flash. + private static final int FLASH_DURATION_MS = 300; + + // Commonly used parts of the paths to ignore. + private static final String CONTENT = "DecorView|LinearLayout|FrameLayout:id/content|"; + private static final String DRAG_LAYER = + CONTENT + "LauncherRootView:id/launcher|DragLayer:id/drag_layer|"; + private static final String RECENTS_DRAG_LAYER = + CONTENT + "LauncherRootView:id/launcher|RecentsDragLayer:id/drag_layer|"; + + private static final IgnoreNode IGNORED_NODES_ROOT = buildIgnoreNodesTree(List.of( + CONTENT + "LauncherRootView:id/launcher|FloatingIconView", + DRAG_LAYER + "LauncherRecentsView:id/overview_panel|TaskView|TextView", + DRAG_LAYER + + "LauncherAllAppsContainerView:id/apps_view|AllAppsRecyclerView:id" + + "/apps_list_view|BubbleTextView:id/icon", + CONTENT + + "AddItemDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + + "/add_item_bottom_sheet|LinearLayout:id/add_item_bottom_sheet_content" + + "|ScrollView:id/widget_preview_scroll_view|WidgetCell:id/widget_cell" + + "|WidgetCellPreview:id/widget_preview_container|WidgetImageView:id" + + "/widget_preview", + CONTENT + + "AddItemDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + + "/add_item_bottom_sheet|LinearLayout:id/add_item_bottom_sheet_content" + + "|ScrollView:id/widget_preview_scroll_view|WidgetCell:id/widget_cell" + + "|WidgetCellPreview:id/widget_preview_container|ImageView:id/widget_badge", + RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel|TaskView|IconView:id/icon", + DRAG_LAYER + "SearchContainerView:id/apps_view", + DRAG_LAYER + "LauncherDragView", + DRAG_LAYER + "FloatingTaskView|FloatingTaskThumbnailView:id/thumbnail", + DRAG_LAYER + + "WidgetsFullSheet|SpringRelativeLayout:id/container|WidgetsRecyclerView:id" + + "/primary_widgets_list_view|WidgetsListHeader:id/widgets_list_header", + DRAG_LAYER + + "WidgetsTwoPaneSheet|SpringRelativeLayout:id/container|LinearLayout:id" + + "/linear_layout_container|FrameLayout:id/recycler_view_container" + + "|FrameLayout:id/widgets_two_pane_sheet_recyclerview|WidgetsRecyclerView:id" + + "/primary_widgets_list_view|WidgetsListHeader:id/widgets_list_header" + )); + + // Per-AnalysisNode data that's specific to this detector. + private static class NodeData { + public boolean ignoreFlashes; + + // If ignoreNode is null, then this AnalysisNode node will be ignored if its parent is + // ignored. + // Otherwise, this AnalysisNode will be ignored if ignoreNode is a leaf i.e. has no + // children. + public IgnoreNode ignoreNode; + } + + private NodeData getNodeData(AnalysisNode info) { + return (NodeData) info.detectorsData[detectorOrdinal]; + } + + @Override + void initializeNode(AnalysisNode info) { + final NodeData nodeData = new NodeData(); + info.detectorsData[detectorOrdinal] = nodeData; + + // If the parent view ignores flashes, its descendants will too. + final boolean parentIgnoresFlashes = info.parent != null && getNodeData( + info.parent).ignoreFlashes; + if (parentIgnoresFlashes) { + nodeData.ignoreFlashes = true; + return; + } + + // Parent view doesn't ignore flashes. + // Initialize this AnalysisNode's ignore-node with the corresponding child of the + // ignore-node of the parent, if present. + final IgnoreNode parentIgnoreNode = info.parent != null + ? getNodeData(info.parent).ignoreNode + : IGNORED_NODES_ROOT; + nodeData.ignoreNode = parentIgnoreNode != null + ? parentIgnoreNode.children.get(info.nodeIdentity) : null; + // AnalysisNode will be ignored if the corresponding ignore-node is a leaf. + nodeData.ignoreFlashes = + nodeData.ignoreNode != null && nodeData.ignoreNode.children.isEmpty(); + } + + @Override + String detectAnomalies(AnalysisNode oldInfo, AnalysisNode newInfo, int frameN, + long frameTimeNs, int windowSizePx) { + // Should we check when a view was visible for a short period, then its alpha became 0? + // Then 'lastVisible' time should be the last one still visible? + // Check only transitions of alpha between 0 and 1? + + // If this is the first time ever when we see the view, there have been no flashes yet. + if (oldInfo == null) return null; + + // A flash requires a view to go from the full visibility to no-visibility and then back, + // or vice versa. + // If the last time the view was seen before the current frame, it didn't have full + // visibility; no flash can possibly be detected at the current frame. + if (oldInfo.alpha < 1) return null; + + final AnalysisNode latestInfo = newInfo != null ? newInfo : oldInfo; + final NodeData nodeData = getNodeData(latestInfo); + if (nodeData.ignoreFlashes) return null; + + // Once the view becomes invisible, see for how long it was visible prior to that. If it + // was visible only for a short interval of time, it's a flash. + if ( + // View is invisible in the current frame + newInfo == null + // When the view became visible last time, it was a transition from + // no-visibility to full visibility. + && oldInfo.timeBecameVisibleNs != -1) { + final long wasVisibleTimeMs = (frameTimeNs - oldInfo.timeBecameVisibleNs) / 1000000; + + if (wasVisibleTimeMs <= FLASH_DURATION_MS) { + nodeData.ignoreFlashes = true; // No need to report flashes in children. + return + String.format( + "View was visible for a too short period of time %dms, which is a" + + " flash", + wasVisibleTimeMs + ); + } + } + + // Once a view becomes visible, see for how long it was invisible prior to that. If it + // was invisible only for a short interval of time, it's a flash. + if ( + // The view is fully visible now + newInfo != null && newInfo.alpha >= 1 + // The view wasn't visible in the previous frame + && frameN != oldInfo.frameN + 1) { + // We can assert the below condition because at this point, we know that + // oldInfo.alpha >= 1, i.e. it disappeared abruptly. + assertTrue("oldInfo.timeBecameInvisibleNs must not be -1", + oldInfo.timeBecameInvisibleNs != -1); + + final long wasInvisibleTimeMs = (frameTimeNs - oldInfo.timeBecameInvisibleNs) / 1000000; + if (wasInvisibleTimeMs <= FLASH_DURATION_MS) { + nodeData.ignoreFlashes = true; // No need to report flashes in children. + return + String.format( + "View was invisible for a too short period of time %dms, which " + + "is a flash", + wasInvisibleTimeMs); + } + } + return null; + } +} diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/PositionJumpDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/PositionJumpDetector.java new file mode 100644 index 0000000000..a1ddcb054e --- /dev/null +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/PositionJumpDetector.java @@ -0,0 +1,126 @@ +/* + * 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.launcher3.util.viewcapture_analysis; + +import com.android.launcher3.util.viewcapture_analysis.ViewCaptureAnalyzer.AnalysisNode; + +import java.util.List; + +/** + * Anomaly detector that triggers an error when a view position jumps. + */ +final class PositionJumpDetector extends AnomalyDetector { + // Maximum allowed jump in "milliwindows", i.e. a 1/1000's of the maximum of the window + // dimensions. + private static final float JUMP_MIW = 250; + + private static final String[] BORDER_NAMES = {"left", "top", "right", "bottom"}; + + // Commonly used parts of the paths to ignore. + private static final String CONTENT = "DecorView|LinearLayout|FrameLayout:id/content|"; + private static final String DRAG_LAYER = + CONTENT + "LauncherRootView:id/launcher|DragLayer:id/drag_layer|"; + private static final String RECENTS_DRAG_LAYER = + CONTENT + "LauncherRootView:id/launcher|RecentsDragLayer:id/drag_layer|"; + + private static final IgnoreNode IGNORED_NODES_ROOT = buildIgnoreNodesTree(List.of( + DRAG_LAYER + "SearchContainerView:id/apps_view", + DRAG_LAYER + "AppWidgetResizeFrame", + DRAG_LAYER + "LauncherAllAppsContainerView:id/apps_view", + CONTENT + + "AddItemDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + + "/add_item_bottom_sheet|LinearLayout:id/add_item_bottom_sheet_content", + DRAG_LAYER + "WidgetsTwoPaneSheet|SpringRelativeLayout:id/container", + DRAG_LAYER + "WidgetsFullSheet|SpringRelativeLayout:id/container", + DRAG_LAYER + "LauncherDragView", + RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel|TaskView", + CONTENT + "LauncherRootView:id/launcher|FloatingIconView", + DRAG_LAYER + "FloatingTaskView", + DRAG_LAYER + "LauncherRecentsView:id/overview_panel" + )); + + // Per-AnalysisNode data that's specific to this detector. + private static class NodeData { + public boolean ignoreJumps; + + // If ignoreNode is null, then this AnalysisNode node will be ignored if its parent is + // ignored. + // Otherwise, this AnalysisNode will be ignored if ignoreNode is a leaf i.e. has no + // children. + public IgnoreNode ignoreNode; + } + + private NodeData getNodeData(AnalysisNode info) { + return (NodeData) info.detectorsData[detectorOrdinal]; + } + + @Override + void initializeNode(AnalysisNode info) { + final NodeData nodeData = new NodeData(); + info.detectorsData[detectorOrdinal] = nodeData; + + // If the parent view ignores jumps, its descendants will too. + final boolean parentIgnoresJumps = info.parent != null && getNodeData( + info.parent).ignoreJumps; + if (parentIgnoresJumps) { + nodeData.ignoreJumps = true; + return; + } + + // Parent view doesn't ignore jumps. + // Initialize this AnalysisNode's ignore-node with the corresponding child of the + // ignore-node of the parent, if present. + final IgnoreNode parentIgnoreNode = info.parent != null + ? getNodeData(info.parent).ignoreNode + : IGNORED_NODES_ROOT; + nodeData.ignoreNode = parentIgnoreNode != null + ? parentIgnoreNode.children.get(info.nodeIdentity) : null; + // AnalysisNode will be ignored if the corresponding ignore-node is a leaf. + nodeData.ignoreJumps = + nodeData.ignoreNode != null && nodeData.ignoreNode.children.isEmpty(); + } + + @Override + String detectAnomalies(AnalysisNode oldInfo, AnalysisNode newInfo, int frameN, + long frameTimeNs, int windowSizePx) { + // If the view is not present in the current frame, there can't be a jump detected in the + // current frame. + if (newInfo == null) return null; + + // We only detect position jumps if the view was visible in the previous frame. + if (oldInfo == null || frameN != oldInfo.frameN + 1) return null; + + final NodeData newNodeData = getNodeData(newInfo); + if (newNodeData.ignoreJumps) return null; + + final float[] positionDiffs = { + newInfo.left - oldInfo.left, + newInfo.top - oldInfo.top, + newInfo.right - oldInfo.right, + newInfo.bottom - oldInfo.bottom + }; + + for (int i = 0; i < 4; ++i) { + final float positionDiffAbs = Math.abs(positionDiffs[i]); + if (positionDiffAbs * 1000 > JUMP_MIW * windowSizePx) { + newNodeData.ignoreJumps = true; + return String.format("Position jump: %s jumped by %s", + BORDER_NAMES[i], positionDiffAbs); + } + } + return null; + } +} diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java new file mode 100644 index 0000000000..9459cc2d13 --- /dev/null +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java @@ -0,0 +1,315 @@ +/* + * 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.launcher3.util.viewcapture_analysis; + +import static android.view.View.VISIBLE; + +import com.android.app.viewcapture.data.ExportedData; +import com.android.app.viewcapture.data.FrameData; +import com.android.app.viewcapture.data.ViewNode; +import com.android.app.viewcapture.data.WindowData; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * Utility that analyzes ViewCapture data and finds anomalies such as views appearing or + * disappearing without alpha-fading. + */ +public class ViewCaptureAnalyzer { + private static final String SCRIM_VIEW_CLASS = "com.android.launcher3.views.ScrimView"; + + // All detectors. They will be invoked in the order listed here. + private static final AnomalyDetector[] ANOMALY_DETECTORS = { + new AlphaJumpDetector(), + new FlashDetector(), + new PositionJumpDetector() + }; + + static { + for (int i = 0; i < ANOMALY_DETECTORS.length; ++i) ANOMALY_DETECTORS[i].detectorOrdinal = i; + } + + // A view from view capture data converted to a form that's convenient for detecting anomalies. + static class AnalysisNode { + public String className; + public String resourceId; + public AnalysisNode parent; + + // Window coordinates of the view. + public float left; + public float top; + public float right; + public float bottom; + + // Visible scale and alpha, build recursively from the ancestor list. + public float scaleX; + public float scaleY; + public float alpha; // Always > 0 + + public int frameN; + + // Timestamp of the frame when this view became abruptly visible, i.e. its alpha became 1 + // the next frame after it was 0 or the view wasn't visible. + // If the view is currently invisible or the last appearance wasn't abrupt, the value is -1. + public long timeBecameVisibleNs; + + // Timestamp of the frame when this view became abruptly invisible last time, i.e. its + // alpha became 0, or view disappeared, after being 1 in the previous frame. + // If the view is currently visible or the last disappearance wasn't abrupt, the value is + // -1. + public long timeBecameInvisibleNs; + + public ViewNode viewCaptureNode; + + // Class name + resource id + public String nodeIdentity; + + // Collection of detector-specific data for this node. + public final Object[] detectorsData = new Object[ANOMALY_DETECTORS.length]; + + @Override + public String toString() { + return String.format("view window coordinates: (%s, %s, %s, %s)", + left, top, right, bottom); + } + } + + /** + * Scans a view capture record and searches for view animation anomalies. Can find anomalies for + * multiple views. + * Returns a map from the view path to the anomaly message for the view. Non-empty map means + * that anomalies were detected. + */ + public static Map<String, String> getAnomalies(ExportedData viewCaptureData) { + final Map<String, String> anomalies = new HashMap<>(); + + final int scrimClassIndex = viewCaptureData.getClassnameList().indexOf(SCRIM_VIEW_CLASS); + + final int windowDataCount = viewCaptureData.getWindowDataCount(); + for (int i = 0; i < windowDataCount; ++i) { + analyzeWindowData( + viewCaptureData, viewCaptureData.getWindowData(i), scrimClassIndex, anomalies); + } + return anomalies; + } + + private static void analyzeWindowData(ExportedData viewCaptureData, WindowData windowData, + int scrimClassIndex, Map<String, String> anomalies) { + // View hash code => Last seen node with this hash code. + // The view is added when we analyze the first frame where it's visible. + // After that, it gets updated for every frame where it's visible. + // As we go though frames, if a view becomes invisible, it stays in the map. + final Map<Integer, AnalysisNode> lastSeenNodes = new HashMap<>(); + + int windowWidthPx = -1; + int windowHeightPx = -1; + + for (int frameN = 0; frameN < windowData.getFrameDataCount(); ++frameN) { + final FrameData frame = windowData.getFrameData(frameN); + final ViewNode rootNode = frame.getNode(); + + // If the rotation or window size has changed, reset the analyzer state. + final boolean isFirstFrame = windowWidthPx != rootNode.getWidth() + || windowHeightPx != rootNode.getHeight(); + if (isFirstFrame) { + windowWidthPx = rootNode.getWidth(); + windowHeightPx = rootNode.getHeight(); + lastSeenNodes.clear(); + } + + final int windowSizePx = Math.max(rootNode.getWidth(), rootNode.getHeight()); + + analyzeFrame(frameN, isFirstFrame, frame, viewCaptureData, lastSeenNodes, + scrimClassIndex, anomalies, windowSizePx); + } + } + + private static void analyzeFrame(int frameN, boolean isFirstFrame, FrameData frame, + ExportedData viewCaptureData, + Map<Integer, AnalysisNode> lastSeenNodes, int scrimClassIndex, + Map<String, String> anomalies, int windowSizePx) { + // Analyze the node tree starting from the root. + long frameTimeNs = frame.getTimestamp(); + analyzeView( + frameTimeNs, + frame.getNode(), + /* parent = */ null, + frameN, + isFirstFrame, + /* leftShift = */ 0, + /* topShift = */ 0, + viewCaptureData, + lastSeenNodes, + scrimClassIndex, + anomalies, + windowSizePx); + + // Analyze transitions when a view visible in the previous frame became invisible in the + // current one. + for (AnalysisNode info : lastSeenNodes.values()) { + if (info.frameN == frameN - 1) { + if (!info.viewCaptureNode.getWillNotDraw()) { + Arrays.stream(ANOMALY_DETECTORS).forEach( + detector -> + detectAnomaly( + detector, + frameN, + /* oldInfo = */ info, + /* newInfo = */ null, + anomalies, + frameTimeNs, + windowSizePx) + ); + } + info.timeBecameInvisibleNs = info.alpha == 1 ? frameTimeNs : -1; + info.timeBecameVisibleNs = -1; + } + } + } + + private static void analyzeView(long frameTimeNs, ViewNode viewCaptureNode, AnalysisNode parent, + int frameN, + boolean isFirstFrame, float leftShift, float topShift, ExportedData viewCaptureData, + Map<Integer, AnalysisNode> lastSeenNodes, int scrimClassIndex, + Map<String, String> anomalies, int windowSizePx) { + // Skip analysis of invisible views + final float parentAlpha = parent != null ? parent.alpha : 1; + final float alpha = getVisibleAlpha(viewCaptureNode, parentAlpha); + if (alpha <= 0.0) return; + + // Calculate analysis node parameters + final int hashcode = viewCaptureNode.getHashcode(); + final int classIndex = viewCaptureNode.getClassnameIndex(); + + final float parentScaleX = parent != null ? parent.scaleX : 1; + final float parentScaleY = parent != null ? parent.scaleY : 1; + final float scaleX = parentScaleX * viewCaptureNode.getScaleX(); + final float scaleY = parentScaleY * viewCaptureNode.getScaleY(); + + final float left = leftShift + + (viewCaptureNode.getLeft() + viewCaptureNode.getTranslationX()) * parentScaleX + + viewCaptureNode.getWidth() * (parentScaleX - scaleX) / 2; + final float top = topShift + + (viewCaptureNode.getTop() + viewCaptureNode.getTranslationY()) * parentScaleY + + viewCaptureNode.getHeight() * (parentScaleY - scaleY) / 2; + final float width = viewCaptureNode.getWidth() * scaleX; + final float height = viewCaptureNode.getHeight() * scaleY; + + // Initialize new analysis node + final AnalysisNode newAnalysisNode = new AnalysisNode(); + newAnalysisNode.className = viewCaptureData.getClassname(classIndex); + newAnalysisNode.resourceId = viewCaptureNode.getId(); + newAnalysisNode.nodeIdentity = + getNodeIdentity(newAnalysisNode.className, newAnalysisNode.resourceId); + newAnalysisNode.parent = parent; + newAnalysisNode.left = left; + newAnalysisNode.top = top; + newAnalysisNode.right = left + width; + newAnalysisNode.bottom = top + height; + newAnalysisNode.scaleX = scaleX; + newAnalysisNode.scaleY = scaleY; + newAnalysisNode.alpha = alpha; + newAnalysisNode.frameN = frameN; + newAnalysisNode.timeBecameInvisibleNs = -1; + newAnalysisNode.viewCaptureNode = viewCaptureNode; + Arrays.stream(ANOMALY_DETECTORS).forEach( + detector -> detector.initializeNode(newAnalysisNode)); + + final AnalysisNode oldAnalysisNode = lastSeenNodes.get(hashcode); // may be null + + if (oldAnalysisNode != null && oldAnalysisNode.frameN + 1 == frameN) { + // If this view was present in the previous frame, keep the time when it became visible. + newAnalysisNode.timeBecameVisibleNs = oldAnalysisNode.timeBecameVisibleNs; + } else { + // If the view is becoming visible after being invisible, initialize the time when it + // became visible with a new value. + // If the view became visible abruptly, i.e. alpha jumped from 0 to 1 between the + // previous and the current frames, then initialize with the time of the current + // frame. Otherwise, use -1. + newAnalysisNode.timeBecameVisibleNs = newAnalysisNode.alpha >= 1 ? frameTimeNs : -1; + } + + // Detect anomalies for the view. + if (!isFirstFrame && !viewCaptureNode.getWillNotDraw()) { + Arrays.stream(ANOMALY_DETECTORS).forEach( + detector -> + detectAnomaly(detector, frameN, oldAnalysisNode, newAnalysisNode, + anomalies, frameTimeNs, windowSizePx) + ); + } + lastSeenNodes.put(hashcode, newAnalysisNode); + + // Enumerate children starting from the topmost one. Stop at ScrimView, if present. + final float leftShiftForChildren = left - viewCaptureNode.getScrollX(); + final float topShiftForChildren = top - viewCaptureNode.getScrollY(); + for (int i = viewCaptureNode.getChildrenCount() - 1; i >= 0; --i) { + final ViewNode child = viewCaptureNode.getChildren(i); + + // Don't analyze anything under scrim view because we don't know whether it's + // transparent. + if (child.getClassnameIndex() == scrimClassIndex) break; + + analyzeView(frameTimeNs, child, newAnalysisNode, frameN, isFirstFrame, + leftShiftForChildren, + topShiftForChildren, + viewCaptureData, lastSeenNodes, scrimClassIndex, anomalies, windowSizePx); + } + } + + private static void detectAnomaly(AnomalyDetector detector, int frameN, + AnalysisNode oldAnalysisNode, AnalysisNode newAnalysisNode, + Map<String, String> anomalies, long frameTimeNs, int windowSizePx) { + final String maybeAnomaly = + detector.detectAnomalies(oldAnalysisNode, newAnalysisNode, frameN, frameTimeNs, + windowSizePx); + if (maybeAnomaly != null) { + AnalysisNode latestInfo = newAnalysisNode != null ? newAnalysisNode : oldAnalysisNode; + final String viewDiagPath = diagPathFromRoot(latestInfo); + if (!anomalies.containsKey(viewDiagPath)) { + anomalies.put(viewDiagPath, String.format("%s, %s", maybeAnomaly, latestInfo)); + } + } + } + + private static float getVisibleAlpha(ViewNode node, float parenVisibleAlpha) { + return node.getVisibility() == VISIBLE + ? parenVisibleAlpha * Math.max(0, Math.min(node.getAlpha(), 1)) + : 0f; + } + + private static String classNameToSimpleName(String className) { + return className.substring(className.lastIndexOf(".") + 1); + } + + private static String diagPathFromRoot(AnalysisNode analysisNode) { + final StringBuilder path = new StringBuilder(analysisNode.nodeIdentity); + for (AnalysisNode ancestor = analysisNode.parent; + ancestor != null; + ancestor = ancestor.parent) { + path.insert(0, ancestor.nodeIdentity + "|"); + } + return path.toString(); + } + + private static String getNodeIdentity(String className, String resourceId) { + final StringBuilder sb = new StringBuilder(); + sb.append(classNameToSimpleName(className)); + if (!"NO_ID".equals(resourceId)) sb.append(":" + resourceId); + return sb.toString(); + } +} diff --git a/tests/src/com/android/launcher3/widget/picker/OWNERS b/tests/src/com/android/launcher3/widget/picker/OWNERS new file mode 100644 index 0000000000..775b0c7d9b --- /dev/null +++ b/tests/src/com/android/launcher3/widget/picker/OWNERS @@ -0,0 +1,18 @@ +set noparent + +# Bug component: 1481801 +# People who can approve changes for submission +# + +# Widget Picker OWNERS +zakcohen@google.com +shamalip@google.com +wvk@google.com + +# For Tests +vadimt@google.com + +# Launcher OWNERS +captaincole@google.com +sunnygoyal@google.com + diff --git a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java index 10afe13d62..425c3c00f9 100644 --- a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java +++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java @@ -45,8 +45,8 @@ public class AddToHomeScreenPrompt { mLauncher.clickObject( mLauncher.waitForObjectInContainer( mWidgetCell.getParent().getParent().getParent().getParent(), - By.text(ADD_AUTOMATICALLY)), - LauncherInstrumentation.GestureScope.OUTSIDE_WITHOUT_PILFER); + By.text(ADD_AUTOMATICALLY)) + ); mLauncher.waitUntilLauncherObjectGone(getSelector()); } } diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java index 399abc7ad2..fb08ea44eb 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java @@ -210,6 +210,9 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer { public AppIcon getAppIcon(String appName) { AppIcon appIcon = tryGetAppIcon(appName); mLauncher.assertNotNull("Unable to scroll to a clickable icon: " + appName, appIcon); + // appIcon.getAppName() checks for content description, so it is possible that it can have + // trailing words. So check if the content description contains the appName. + mLauncher.assertTrue("Wrong app icon name.", appIcon.getAppName().contains(appName)); return appIcon; } @@ -333,4 +336,11 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer { final Bundle testInfo = mLauncher.getTestInfo(TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS); return testInfo == null ? 0 : testInfo.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); } + + /** + * Return the QSB UI object on the AllApps screen. + * @return the QSB UI object. + */ + @NonNull + public abstract Qsb getQsb(); }
\ No newline at end of file diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java index c4744a1e67..0e0291f82c 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java +++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java @@ -62,4 +62,10 @@ public class AllAppsFromTaskbar extends AllApps { return mLauncher.getTestInfo(TestProtocol.REQUEST_TASKBAR_APPS_LIST_SCROLL_Y) .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); } + + @NonNull + @Override + public TaskbarAllAppsQsb getQsb() { + return new TaskbarAllAppsQsb(mLauncher, verifyActiveContainer()); + } } diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java b/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java index 0931cd46b0..1692351181 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java +++ b/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java @@ -22,16 +22,7 @@ import androidx.test.uiautomator.UiObject2; */ class AllAppsQsb extends Qsb { - private final UiObject2 mAllAppsContainer; - AllAppsQsb(LauncherInstrumentation launcher, UiObject2 allAppsContainer) { - super(launcher); - mAllAppsContainer = allAppsContainer; - waitForQsbObject(); - } - - @Override - protected UiObject2 waitForQsbObject() { - return mLauncher.waitForObjectInContainer(mAllAppsContainer, "search_container_all_apps"); + super(launcher, allAppsContainer, "search_container_all_apps"); } } diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java index 0a0cf07482..85098c899c 100644 --- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java +++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java @@ -37,10 +37,14 @@ public abstract class AppIcon extends Launchable { } static BySelector getAppIconSelector(String appName, LauncherInstrumentation launcher) { - return By.clazz(TextView.class).textContains(appName) + return By.clazz(TextView.class).desc(makeMultilinePattern(appName)) .pkg(launcher.getLauncherPackageName()); } + static BySelector getMenuItemSelector(String text, LauncherInstrumentation launcher) { + return By.clazz(TextView.class).text(text).pkg(launcher.getLauncherPackageName()); + } + static BySelector getAnyAppIconSelector() { return By.clazz(TextView.class); } @@ -94,4 +98,24 @@ public abstract class AppIcon extends Launchable { public String getIconName() { return getObject().getText(); } + + /** + * Return the app name of a icon by the content description. This should be used when trying to + * get the name of an app where the text of it is multiline. + */ + @NonNull + String getAppName() { + return getObject().getContentDescription(); + } + + /** + * Create a regular expression pattern that matches strings starting with the app name, where + * spaces in the app name are replaced with zero or more occurrences of the "\s" character + * (which represents a whitespace character in regular expressions), followed by any characters + * after the app name. + */ + static Pattern makeMultilinePattern(String appName) { + return Pattern.compile(appName.replaceAll("\\s+", "\\\\s*") + ".*", + Pattern.DOTALL); + } } diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java index 667290f0a2..bbcc6a85ba 100644 --- a/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java +++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java @@ -50,7 +50,7 @@ public abstract class AppIconMenu { */ public AppIconMenuItem getMenuItem(String shortcutText) { final UiObject2 menuItem = mLauncher.waitForObjectInContainer(mDeepShortcutsContainer, - AppIcon.getAppIconSelector(shortcutText, mLauncher)); + AppIcon.getMenuItemSelector(shortcutText, mLauncher)); return createMenuItem(menuItem); } @@ -59,7 +59,7 @@ public abstract class AppIconMenu { */ public SplitScreenMenuItem getSplitScreenMenuItem() { final UiObject2 menuItem = mLauncher.waitForObjectInContainer(mDeepShortcutsContainer, - AppIcon.getAppIconSelector("Split screen", mLauncher)); + AppIcon.getMenuItemSelector("Split screen", mLauncher)); return new SplitScreenMenuItem(mLauncher, menuItem); } diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java index 5a96d95316..677f204748 100644 --- a/tests/tapl/com/android/launcher3/tapl/Background.java +++ b/tests/tapl/com/android/launcher3/tapl/Background.java @@ -28,6 +28,8 @@ import android.view.MotionEvent; import androidx.annotation.NonNull; import androidx.test.uiautomator.UiObject2; +import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel; +import com.android.launcher3.tapl.LauncherInstrumentation.TrackpadGestureType; import com.android.launcher3.testing.shared.TestProtocol; import java.util.List; @@ -64,85 +66,71 @@ public abstract class Background extends LauncherInstrumentation.VisibleContaine } - protected boolean zeroButtonToOverviewGestureStartsInLauncher() { - return mLauncher.isTablet(); - } - protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() { return false; } protected void goToOverviewUnchecked() { - switch (mLauncher.getNavigationModel()) { - case ZERO_BUTTON: { - final long downTime = SystemClock.uptimeMillis(); - sendDownPointerToEnterOverviewToLauncher(downTime); - String swipeAndHoldToEnterOverviewActionName = - "swiping and holding to enter overview"; - // If swiping from an app (e.g. Overview is in Background), we pause and hold on - // swipe up to make overview appear, or else swiping without holding would take - // us to the Home state. If swiping up from Home (e.g. Overview in Home or - // Workspace state where the below condition is true), there is no need to pause, - // and we will not test for an intermediate carousel as one will not exist. - if (zeroButtonToOverviewGestureStateTransitionWhileHolding()) { - mLauncher.runToState( - () -> sendSwipeUpAndHoldToEnterOverviewGestureToLauncher(downTime), - OVERVIEW_STATE_ORDINAL, swipeAndHoldToEnterOverviewActionName); - sendUpPointerToEnterOverviewToLauncher(downTime); - } else { - // If swiping up from an app to overview, pause on intermediate carousel - // until snapshots are visible. No intermediate carousel when swiping from - // Home. The task swiped up is not a snapshot but the TaskViewSimulator. If - // only a single task exists, no snapshots will be available during swipe up. - mLauncher.executeAndWaitForLauncherEvent( - () -> sendSwipeUpAndHoldToEnterOverviewGestureToLauncher(downTime), - event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals( - event.getClassName().toString()), - () -> "Pause wasn't detected", - swipeAndHoldToEnterOverviewActionName); - try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( - "paused on swipe up to overview")) { - if (mLauncher.getRecentTasks().size() > 1) { - // When swiping up to grid-overview for tablets, the swiped tab will be - // in the middle of the screen (TaskViewSimulator, not a snapshot), and - // all remaining snapshots will be to the left of that task. In - // non-tablet overview, snapshots can be on either side of the swiped - // task, but we still check that they become visible after swiping and - // pausing. - mLauncher.waitForOverviewObject("snapshot"); - if (mLauncher.isTablet()) { - List<UiObject2> tasks = mLauncher.getDevice().findObjects( - mLauncher.getOverviewObjectSelector("snapshot")); - final int centerX = mLauncher.getDevice().getDisplayWidth() / 2; - mLauncher.assertTrue( - "All tasks not to the left of the swiped task", - tasks.stream() - .allMatch( - t -> t.getVisibleBounds().right < centerX)); - } - + if (mLauncher.getNavigationModel() == NavigationModel.ZERO_BUTTON + || mLauncher.getTrackpadGestureType() == TrackpadGestureType.THREE_FINGER) { + final long downTime = SystemClock.uptimeMillis(); + sendDownPointerToEnterOverviewToLauncher(downTime); + String swipeAndHoldToEnterOverviewActionName = + "swiping and holding to enter overview"; + // If swiping from an app (e.g. Overview is in Background), we pause and hold on + // swipe up to make overview appear, or else swiping without holding would take + // us to the Home state. If swiping up from Home (e.g. Overview in Home or + // Workspace state where the below condition is true), there is no need to pause, + // and we will not test for an intermediate carousel as one will not exist. + if (zeroButtonToOverviewGestureStateTransitionWhileHolding()) { + mLauncher.runToState( + () -> sendSwipeUpAndHoldToEnterOverviewGestureToLauncher(downTime), + OVERVIEW_STATE_ORDINAL, swipeAndHoldToEnterOverviewActionName); + sendUpPointerToEnterOverviewToLauncher(downTime); + } else { + // If swiping up from an app to overview, pause on intermediate carousel + // until snapshots are visible. No intermediate carousel when swiping from + // Home. The task swiped up is not a snapshot but the TaskViewSimulator. If + // only a single task exists, no snapshots will be available during swipe up. + mLauncher.executeAndWaitForLauncherEvent( + () -> sendSwipeUpAndHoldToEnterOverviewGestureToLauncher(downTime), + event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals( + event.getClassName().toString()), + () -> "Pause wasn't detected", + swipeAndHoldToEnterOverviewActionName); + try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "paused on swipe up to overview")) { + if (mLauncher.getRecentTasks().size() > 1) { + // When swiping up to grid-overview for tablets, the swiped tab will be + // in the middle of the screen (TaskViewSimulator, not a snapshot), and + // all remaining snapshots will be to the left of that task. In + // non-tablet overview, snapshots can be on either side of the swiped + // task, but we still check that they become visible after swiping and + // pausing. + mLauncher.waitForOverviewObject("snapshot"); + if (mLauncher.isTablet()) { + List<UiObject2> tasks = mLauncher.getDevice().findObjects( + mLauncher.getOverviewObjectSelector("snapshot")); + final int centerX = mLauncher.getDevice().getDisplayWidth() / 2; + mLauncher.assertTrue( + "All tasks not to the left of the swiped task", + tasks.stream() + .allMatch( + t -> t.getVisibleBounds().right < centerX)); } - String upPointerToEnterOverviewActionName = - "sending UP pointer to enter overview"; - mLauncher.runToState(() -> sendUpPointerToEnterOverviewToLauncher(downTime), - OVERVIEW_STATE_ORDINAL, upPointerToEnterOverviewActionName); + } + String upPointerToEnterOverviewActionName = + "sending UP pointer to enter overview"; + mLauncher.runToState(() -> sendUpPointerToEnterOverviewToLauncher(downTime), + OVERVIEW_STATE_ORDINAL, upPointerToEnterOverviewActionName); } - break; } - - case THREE_BUTTON: - if (mLauncher.isTablet()) { - mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, - LauncherInstrumentation.EVENT_TOUCH_DOWN); - mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, - LauncherInstrumentation.EVENT_TOUCH_UP); - } - mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT); - mLauncher.runToState( - () -> mLauncher.waitForNavigationUiObject("recent_apps").click(), - OVERVIEW_STATE_ORDINAL, "clicking Recents button"); - break; + } else { + mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT); + mLauncher.runToState( + () -> mLauncher.waitForNavigationUiObject("recent_apps").click(), + OVERVIEW_STATE_ORDINAL, "clicking Recents button"); } expectSwitchToOverviewEvents(); } @@ -154,12 +142,9 @@ public abstract class Background extends LauncherInstrumentation.VisibleContaine final int centerX = mLauncher.getDevice().getDisplayWidth() / 2; final int startY = getSwipeStartY(); final Point start = new Point(centerX, startY); - final LauncherInstrumentation.GestureScope gestureScope = - zeroButtonToOverviewGestureStartsInLauncher() - ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE - : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER; - mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start, + LauncherInstrumentation.GestureScope.EXPECT_PILFER); if (!mLauncher.isLauncher3()) { mLauncher.expectEvent(TestProtocol.SEQUENCE_PILFER, @@ -175,10 +160,6 @@ public abstract class Background extends LauncherInstrumentation.VisibleContaine final Point start = new Point(centerX, startY); final Point end = new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop()); - final LauncherInstrumentation.GestureScope gestureScope = - zeroButtonToOverviewGestureStartsInLauncher() - ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE - : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER; mLauncher.movePointer( downTime, @@ -186,7 +167,7 @@ public abstract class Background extends LauncherInstrumentation.VisibleContaine ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION, start, end, - gestureScope); + LauncherInstrumentation.GestureScope.EXPECT_PILFER); } private void sendUpPointerToEnterOverviewToLauncher(long downTime) { @@ -197,13 +178,9 @@ public abstract class Background extends LauncherInstrumentation.VisibleContaine final Point end = new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop()); - final LauncherInstrumentation.GestureScope gestureScope = - zeroButtonToOverviewGestureStartsInLauncher() - ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER - : LauncherInstrumentation.GestureScope.OUTSIDE_WITHOUT_PILFER; - mLauncher.sendPointer(downTime, SystemClock.uptimeMillis(), - MotionEvent.ACTION_UP, end, gestureScope); + MotionEvent.ACTION_UP, end, + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); } /** @@ -235,71 +212,48 @@ public abstract class Background extends LauncherInstrumentation.VisibleContaine LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "want to quick switch to the previous app")) { verifyActiveContainer(); - final boolean launcherWasVisible = mLauncher.isLauncherVisible(); - switch (mLauncher.getNavigationModel()) { - case ZERO_BUTTON: { - final int startX; - final int startY; - final int endX; - final int endY; - final int cornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius()); - if (toRight) { - // Swipe from the bottom left to the bottom right of the screen. - startX = cornerRadius; - startY = getSwipeStartY(); - endX = mLauncher.getDevice().getDisplayWidth() - cornerRadius; - endY = startY; - } else { - // Swipe from the bottom right to the bottom left of the screen. - startX = mLauncher.getDevice().getDisplayWidth() - cornerRadius; - startY = getSwipeStartY(); - endX = cornerRadius; - endY = startY; - } - - final boolean isZeroButton = mLauncher.getNavigationModel() - == LauncherInstrumentation.NavigationModel.ZERO_BUTTON; - LauncherInstrumentation.GestureScope gestureScope = - launcherWasVisible && isZeroButton - ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE - : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER; - mLauncher.executeAndWaitForEvent( - () -> mLauncher.linearGesture( - startX, startY, endX, endY, 20, false, gestureScope), - event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED, - () -> "Quick switch gesture didn't change window state", "swiping"); - break; + if (mLauncher.getNavigationModel() == NavigationModel.ZERO_BUTTON + || mLauncher.getTrackpadGestureType() == TrackpadGestureType.FOUR_FINGER) { + final int startX; + final int startY; + final int endX; + final int endY; + final int cornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius()); + if (toRight) { + // Swipe from the bottom left to the bottom right of the screen. + startX = cornerRadius; + startY = getSwipeStartY(); + endX = mLauncher.getDevice().getDisplayWidth() - cornerRadius; + endY = startY; + } else { + // Swipe from the bottom right to the bottom left of the screen. + startX = mLauncher.getDevice().getDisplayWidth() - cornerRadius; + startY = getSwipeStartY(); + endX = cornerRadius; + endY = startY; } - case THREE_BUTTON: - // Double press the recents button. - UiObject2 recentsButton = mLauncher.waitForNavigationUiObject("recent_apps"); - if (mLauncher.isTablet()) { - mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, - LauncherInstrumentation.EVENT_TOUCH_DOWN); - mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, - LauncherInstrumentation.EVENT_TOUCH_UP); - } - mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT); - mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL, - "clicking Recents button for the first time"); - mLauncher.getOverview(); - if (mLauncher.isTablet()) { - mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, - LauncherInstrumentation.EVENT_TOUCH_DOWN); - mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, - LauncherInstrumentation.EVENT_TOUCH_UP); - } - mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT); - mLauncher.executeAndWaitForEvent( - () -> recentsButton.click(), - event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED, - () -> "Pressing recents button didn't change window state", - "clicking Recents button for the second time"); - break; + mLauncher.executeAndWaitForEvent( + () -> mLauncher.linearGesture( + startX, startY, endX, endY, 20, false, + LauncherInstrumentation.GestureScope.EXPECT_PILFER), + event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED, + () -> "Quick switch gesture didn't change window state", "swiping"); + } else { + // Double press the recents button. + UiObject2 recentsButton = mLauncher.waitForNavigationUiObject("recent_apps"); + mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT); + mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL, + "clicking Recents button for the first time"); + mLauncher.getOverview(); + mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT); + mLauncher.executeAndWaitForEvent( + () -> recentsButton.click(), + event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED, + () -> "Pressing recents button didn't change window state", + "clicking Recents button for the second time"); } mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT); - return; } } @@ -312,6 +266,8 @@ public abstract class Background extends LauncherInstrumentation.VisibleContaine } protected int getSwipeStartY() { - return mLauncher.getRealDisplaySize().y - 1; + return mLauncher.getTrackpadGestureType() == TrackpadGestureType.THREE_FINGER + ? mLauncher.getDevice().getDisplayHeight() * 3 / 4 + : mLauncher.getRealDisplaySize().y - 1; } } diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java index 2c3c02827d..aa5c770ab9 100644 --- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java +++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java @@ -324,13 +324,16 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { if (!hasTasks() || isClearAllVisible()) { return false; } - OverviewTask task = mLauncher.isTablet() ? getFocusedTaskForTablet() : getCurrentTask(); + boolean isTablet = mLauncher.isTablet(); + if (isTablet && mLauncher.isGridOnlyOverviewEnabled()) { + return false; + } + OverviewTask task = isTablet ? getFocusedTaskForTablet() : getCurrentTask(); if (task == null) { return false; } // In tablets, if focused task is not in center, overview actions aren't visible. - if (mLauncher.isTablet() - && Math.abs(task.getExactCenterX() - mLauncher.getExactScreenCenterX()) >= 1) { + if (isTablet && Math.abs(task.getExactCenterX() - mLauncher.getExactScreenCenterX()) >= 1) { return false; } // Overview actions aren't visible for split screen tasks. diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java index ee9dd1aca0..252435badb 100644 --- a/tests/tapl/com/android/launcher3/tapl/Home.java +++ b/tests/tapl/com/android/launcher3/tapl/Home.java @@ -59,11 +59,6 @@ public abstract class Home extends Background { } @Override - protected boolean zeroButtonToOverviewGestureStartsInLauncher() { - return true; - } - - @Override protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() { return true; } diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java index 1239d7a0b3..295190146f 100644 --- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java @@ -30,23 +30,28 @@ public class HomeAllApps extends AllApps { } /** - * Swipes down to Workspace. + * Swipes up or down to dismiss to Workspace. + * @param swipeDown Swipe all apps down to dismiss, otherwise swipe up to dismiss by going home. * * @return the Workspace object. */ @NonNull - public Workspace switchToWorkspace() { + public Workspace switchToWorkspace(boolean swipeDown) { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); LauncherInstrumentation.Closable c = mLauncher.addContextLayer("want to switch from all apps to workspace")) { UiObject2 allAppsContainer = verifyActiveContainer(); final int startX = allAppsContainer.getVisibleCenter().x; - final int startY = getTopVisibleIconBounds(allAppsContainer).centerY(); - final int endY = mLauncher.getDevice().getDisplayHeight(); + final int startY = swipeDown ? getTopVisibleIconBounds(allAppsContainer).centerY() + : mLauncher.getDevice().getDisplayHeight(); + final int endY = + swipeDown ? mLauncher.getDevice().getDisplayHeight() : getTopVisibleIconBounds( + allAppsContainer).centerY(); LauncherInstrumentation.log( "switchToWorkspace: startY = " + startY + ", endY = " + endY - + ", slop = " + mLauncher.getTouchSlop()); + + ", slop = " + mLauncher.getTouchSlop() + + ", swipeDown = " + swipeDown); mLauncher.swipeToState( startX, @@ -54,7 +59,9 @@ public class HomeAllApps extends AllApps { startX, endY, 5 /* steps */, - NORMAL_STATE_ORDINAL, LauncherInstrumentation.GestureScope.INSIDE); + NORMAL_STATE_ORDINAL, + swipeDown ? LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER + : LauncherInstrumentation.GestureScope.EXPECT_PILFER); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( "swiped to workspace")) { @@ -110,11 +117,8 @@ public class HomeAllApps extends AllApps { } } - /** - * Return the QSB UI object on the AllApps screen. - * @return the QSB UI object. - */ @NonNull + @Override public Qsb getQsb() { return new AllAppsQsb(mLauncher, verifyActiveContainer()); } diff --git a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java index 20d09a1e16..5385c65165 100644 --- a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java +++ b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java @@ -22,16 +22,7 @@ import androidx.test.uiautomator.UiObject2; */ class HomeQsb extends Qsb { - private final UiObject2 mHotSeat; - HomeQsb(LauncherInstrumentation launcher, UiObject2 hotseat) { - super(launcher); - mHotSeat = hotseat; - waitForQsbObject(); - } - - @Override - protected UiObject2 waitForQsbObject() { - return mLauncher.waitForObjectInContainer(mHotSeat, "search_container_hotseat"); + super(launcher, hotseat, "search_container_hotseat"); } } diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java index 48e327fbf5..a953fa9009 100644 --- a/tests/tapl/com/android/launcher3/tapl/Launchable.java +++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java @@ -152,7 +152,7 @@ public abstract class Launchable { downTime, MotionEvent.ACTION_DOWN, iconCenter, - LauncherInstrumentation.GestureScope.INSIDE); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); LauncherInstrumentation.log("movePointerForStartDrag: sent down"); expectLongClickEvents.run(); waitForLongPressConfirmation(); @@ -165,7 +165,7 @@ public abstract class Launchable { downTime, downTime, /* slowDown= */ true, - LauncherInstrumentation.GestureScope.INSIDE); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); } private int getStartDragThreshold() { diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java index a59eff7318..30417c082f 100644 --- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java +++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java @@ -30,6 +30,7 @@ import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_ import android.graphics.Point; import android.graphics.Rect; import android.os.SystemClock; +import android.view.InputDevice; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -37,6 +38,7 @@ import androidx.test.uiautomator.By; import androidx.test.uiautomator.Condition; import androidx.test.uiautomator.UiDevice; +import com.android.launcher3.testing.shared.ResourceUtils; import com.android.launcher3.testing.shared.TestProtocol; /** @@ -50,6 +52,8 @@ public final class LaunchedAppState extends Background { // UNSTASHED_TASKBAR_HANDLE_HINT_SCALE value from TaskbarStashController. private static final float UNSTASHED_TASKBAR_HANDLE_HINT_SCALE = 1.1f; + private static final int STASHED_TASKBAR_BOTTOM_EDGE_DP = 1; + private final Condition<UiDevice, Boolean> mStashedTaskbarHintScaleCondition = device -> mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_SCALE).getFloat( TestProtocol.TEST_INFO_RESPONSE_FIELD) - UNSTASHED_TASKBAR_HANDLE_HINT_SCALE @@ -124,13 +128,13 @@ public final class LaunchedAppState extends Background { mLauncher.getRealDisplaySize().x / 2, unstashTargetY); mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, unstashTarget, - LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER); + LauncherInstrumentation.GestureScope.EXPECT_PILFER); LauncherInstrumentation.log("showTaskbar: sent down"); try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) { mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID); mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, unstashTarget, - LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER); + LauncherInstrumentation.GestureScope.EXPECT_PILFER); return new Taskbar(mLauncher); } @@ -179,7 +183,7 @@ public final class LaunchedAppState extends Background { downTime, SystemClock.uptimeMillis(), /* slowDown= */ false, - LauncherInstrumentation.GestureScope.INSIDE); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); try (LauncherInstrumentation.Closable c3 = launcher.addContextLayer( "moved pointer to drop point")) { @@ -190,7 +194,7 @@ public final class LaunchedAppState extends Background { SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, endPoint, - LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen: " + "after drop"); @@ -209,7 +213,7 @@ public final class LaunchedAppState extends Background { * * <p>This unstashing occurs when not actively hovering the taskbar. */ - public void hoverScreenBottomEdgeToUnstashTaskbar() { + public Taskbar hoverScreenBottomEdgeToUnstashTaskbar() { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "cursor hover entering screen edge to unstash taskbar")) { @@ -220,19 +224,23 @@ public final class LaunchedAppState extends Background { int leftEdge = 10; Point taskbarUnstashArea = new Point(leftEdge, mLauncher.getRealDisplaySize().y - 1); mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER, - new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null); + new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null, + InputDevice.SOURCE_MOUSE); mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID); mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT, - new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null); + new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null, + InputDevice.SOURCE_MOUSE); + + return new Taskbar(mLauncher); } } /** * Emulate the cursor hovering the taskbar to get unstash hint, then hovering below to unstash. */ - public void hoverBelowHintedTaskbarToUnstash() { + public Taskbar hoverBelowHintedTaskbarToUnstash() { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "cursor hover entering stashed taskbar")) { @@ -240,7 +248,8 @@ public final class LaunchedAppState extends Background { Point stashedTaskbarHintArea = new Point(mLauncher.getRealDisplaySize().x / 2, mLauncher.getRealDisplaySize().y - 1); mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER, - new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), null); + new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), null, + InputDevice.SOURCE_MOUSE); mLauncher.getDevice().wait(mStashedTaskbarHintScaleCondition, LauncherInstrumentation.WAIT_TIME_MS); @@ -251,9 +260,11 @@ public final class LaunchedAppState extends Background { Point taskbarUnstashArea = new Point(mLauncher.getRealDisplaySize().x / 2, mLauncher.getRealDisplaySize().y - 1); mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT, - new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null); + new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null, + InputDevice.SOURCE_MOUSE); mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID); + return new Taskbar(mLauncher); } } } @@ -288,4 +299,48 @@ public final class LaunchedAppState extends Background { } } } + + /** + * Emulate the cursor clicking the stashed taskbar to go home. + */ + public Workspace clickStashedTaskbarToGoHome() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "cursor hover entering stashed taskbar")) { + long downTime = SystemClock.uptimeMillis(); + int stashedTaskbarBottomEdge = ResourceUtils.pxFromDp(STASHED_TASKBAR_BOTTOM_EDGE_DP, + mLauncher.getResources().getDisplayMetrics()); + Point stashedTaskbarHintArea = new Point(mLauncher.getRealDisplaySize().x / 2, + mLauncher.getRealDisplaySize().y - stashedTaskbarBottomEdge - 1); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER, + new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), null, + InputDevice.SOURCE_MOUSE); + + mLauncher.getDevice().wait(mStashedTaskbarHintScaleCondition, + LauncherInstrumentation.WAIT_TIME_MS); + + try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( + "cursor clicking stashed taskbar to go home")) { + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT, + new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), + null, InputDevice.SOURCE_MOUSE); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, + new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER, + InputDevice.SOURCE_MOUSE); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_BUTTON_PRESS, + new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), + null, InputDevice.SOURCE_MOUSE); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_BUTTON_RELEASE, + new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), + null, InputDevice.SOURCE_MOUSE); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, + new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER, + InputDevice.SOURCE_MOUSE); + + return mLauncher.getWorkspace(); + } + } + } } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index c4f5da5418..c58ae1608a 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static android.content.pm.PackageManager.DONT_KILL_APP; import static android.content.pm.PackageManager.MATCH_ALL; import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS; +import static android.view.MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT; import static com.android.launcher3.tapl.Folder.FOLDER_CONTENT_RES_ID; import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName; @@ -98,18 +99,9 @@ public final class LauncherInstrumentation { private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 15; private static final int GESTURE_STEP_MS = 16; - static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN"); - static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP"); - private static final Pattern EVENT_TOUCH_CANCEL = getTouchEventPattern("ACTION_CANCEL"); static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers"); static final Pattern EVENT_START = Pattern.compile("start:"); - static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN"); - static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP"); - static final Pattern EVENT_TOUCH_CANCEL_TIS = getTouchEventPatternTIS("ACTION_CANCEL"); - static final Pattern EVENT_HOVER_ENTER_TIS = getTouchEventPatternTIS("ACTION_HOVER_ENTER"); - static final Pattern EVENT_HOVER_EXIT_TIS = getTouchEventPatternTIS("ACTION_HOVER_EXIT"); - private static final Pattern EVENT_KEY_BACK_DOWN = getKeyEventPattern("ACTION_DOWN", "KEYCODE_BACK"); private static final Pattern EVENT_KEY_BACK_UP = @@ -129,26 +121,19 @@ public final class LauncherInstrumentation { public enum NavigationModel {ZERO_BUTTON, THREE_BUTTON} - // Where the gesture happens: outside of Launcher, inside or from inside to outside and - // whether the gesture recognition triggers pilfer. + // Defines whether the gesture recognition triggers pilfer. public enum GestureScope { - OUTSIDE_WITHOUT_PILFER, OUTSIDE_WITH_PILFER, INSIDE, INSIDE_TO_OUTSIDE, - INSIDE_TO_OUTSIDE_WITHOUT_PILFER, - INSIDE_TO_OUTSIDE_WITH_KEYCODE, // For gestures that will trigger a keycode from TIS. - OUTSIDE_WITH_KEYCODE, + DONT_EXPECT_PILFER, + EXPECT_PILFER, } - /** - * Represents a point in the code at which a callback can run. - */ - public enum CALLBACK_RUN_POINT { - CALLBACK_HOLD_BEFORE_DROP, - CALLBACK_HOVER_ENTER, - CALLBACK_HOVER_EXIT, + public enum TrackpadGestureType { + NONE, + TWO_FINGER, + THREE_FINGER, + FOUR_FINGER } - private Consumer<CALLBACK_RUN_POINT> mCallbackAtRunPoint = null; - // Base class for launcher containers. abstract static class VisibleContainer { protected final LauncherInstrumentation mLauncher; @@ -176,7 +161,7 @@ public final class LauncherInstrumentation { void close(); } - private static final String WORKSPACE_RES_ID = "workspace"; + static final String WORKSPACE_RES_ID = "workspace"; private static final String APPS_RES_ID = "apps_view"; private static final String OVERVIEW_RES_ID = "overview_panel"; private static final String WIDGETS_RES_ID = "primary_widgets_list_view"; @@ -208,20 +193,8 @@ public final class LauncherInstrumentation { private boolean mCheckEventsForSuccessfulGestures = false; private Runnable mOnLauncherCrashed; - private static Pattern getTouchEventPattern(String prefix, String action) { - // The pattern includes checks that we don't get a multi-touch events or other surprises. - return Pattern.compile( - prefix + ": MotionEvent.*?action=" + action + ".*?id\\[0\\]=0" - + ".*?toolType\\[0\\]=TOOL_TYPE_FINGER.*?buttonState=0.*?pointerCount=1"); - } - - private static Pattern getTouchEventPattern(String action) { - return getTouchEventPattern("Touch event", action); - } - - private static Pattern getTouchEventPatternTIS(String action) { - return getTouchEventPattern("TouchInteractionService.onInputEvent", action); - } + private TrackpadGestureType mTrackpadGestureType = TrackpadGestureType.NONE; + private int mPointerCount = 0; private static Pattern getKeyEventPattern(String action, String keyCode) { return Pattern.compile("Key event: KeyEvent.*action=" + action + ".*keyCode=" + keyCode); @@ -715,6 +688,19 @@ public final class LauncherInstrumentation { } /** + * Set the trackpad gesture type of the interaction. + * @param trackpadGestureType whether it's not from trackpad, two-finger, three-finger, or + * four-finger gesture. + */ + public void setTrackpadGestureType(TrackpadGestureType trackpadGestureType) { + mTrackpadGestureType = trackpadGestureType; + } + + TrackpadGestureType getTrackpadGestureType() { + return mTrackpadGestureType; + } + + /** * Sets expected rotation. * TAPL periodically checks that Launcher didn't suddenly change the rotation to unexpected one. * Null parameter disables checks. The initial state is "no checks". @@ -957,8 +943,8 @@ public final class LauncherInstrumentation { GestureScope gestureScope = gestureStartFromLauncher // Without the navigation bar layer, the gesture scope on tablets remains inside the // launcher process. - ? (isTablet() ? GestureScope.INSIDE : GestureScope.INSIDE_TO_OUTSIDE) - : GestureScope.OUTSIDE_WITH_PILFER; + ? (isTablet() ? GestureScope.DONT_EXPECT_PILFER : GestureScope.EXPECT_PILFER) + : GestureScope.EXPECT_PILFER; linearGesture( displaySize.x / 2, displaySize.y - 1, displaySize.x / 2, 0, @@ -1003,8 +989,11 @@ public final class LauncherInstrumentation { // We need waiting for any accessibility event generated after pressing Home because // otherwise waitForIdle may return immediately in case when there was a big enough // pause in accessibility events prior to pressing Home. + boolean isThreeFingerTrackpadGesture = + mTrackpadGestureType == TrackpadGestureType.THREE_FINGER; final String action; - if (getNavigationModel() == NavigationModel.ZERO_BUTTON) { + if (getNavigationModel() == NavigationModel.ZERO_BUTTON + || isThreeFingerTrackpadGesture) { checkForAnomaly(false, true); final Point displaySize = getRealDisplaySize(); @@ -1020,21 +1009,19 @@ public final class LauncherInstrumentation { } else { action = "swiping up to home"; + int startY = isThreeFingerTrackpadGesture ? displaySize.y * 3 / 4 + : displaySize.y - 1; + int endY = isThreeFingerTrackpadGesture ? displaySize.y / 4 : displaySize.y / 2; swipeToState( - displaySize.x / 2, displaySize.y - 1, - displaySize.x / 2, displaySize.y / 2, + displaySize.x / 2, startY, + displaySize.x / 2, endY, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, NORMAL_STATE_ORDINAL, - gestureStartFromLauncher ? GestureScope.INSIDE_TO_OUTSIDE - : GestureScope.OUTSIDE_WITH_PILFER); + GestureScope.EXPECT_PILFER); } } else { log("Hierarchy before clicking home:"); dumpViewHierarchy(); action = "clicking home button"; - if (isTablet()) { - expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN); - expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_UP); - } runToState( waitForNavigationUiObject("home")::click, @@ -1059,21 +1046,19 @@ public final class LauncherInstrumentation { waitForLauncherInitialized(); final boolean launcherVisible = isTablet() ? isLauncherContainerVisible() : isLauncherVisible(); - if (getNavigationModel() == NavigationModel.ZERO_BUTTON) { + boolean isThreeFingerTrackpadGesture = + mTrackpadGestureType == TrackpadGestureType.THREE_FINGER; + if (getNavigationModel() == NavigationModel.ZERO_BUTTON + || isThreeFingerTrackpadGesture) { final Point displaySize = getRealDisplaySize(); - final GestureScope gestureScope = - launcherVisible ? GestureScope.INSIDE_TO_OUTSIDE_WITH_KEYCODE - : GestureScope.OUTSIDE_WITH_KEYCODE; // TODO(b/225505986): change startY and endY back to displaySize.y / 2 once the // issue is solved. - linearGesture(0, displaySize.y / 4, displaySize.x / 2, displaySize.y / 4, - 10, false, gestureScope); + int startX = isThreeFingerTrackpadGesture ? displaySize.x / 4 : 0; + int endX = isThreeFingerTrackpadGesture ? displaySize.x * 3 / 4 : displaySize.x / 2; + linearGesture(startX, displaySize.y / 4, endX, displaySize.y / 4, + 10, false, GestureScope.DONT_EXPECT_PILFER); } else { waitForNavigationUiObject("back").click(); - if (isTablet()) { - expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN); - expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_UP); - } } if (launcherVisible) { if (getContext().getApplicationInfo().isOnBackInvokedCallbackEnabled()) { @@ -1481,15 +1466,17 @@ public final class LauncherInstrumentation { * animations because in some scenarios there is a playing animations when the click is * attempted. */ - void clickObject(UiObject2 uiObject, GestureScope gestureScope) { + void clickObject(UiObject2 uiObject) { final long clickTime = SystemClock.uptimeMillis(); final Point center = uiObject.getVisibleCenter(); - sendPointer(clickTime, clickTime, MotionEvent.ACTION_DOWN, center, gestureScope); - sendPointer(clickTime, clickTime, MotionEvent.ACTION_UP, center, gestureScope); + sendPointer(clickTime, clickTime, MotionEvent.ACTION_DOWN, center, + GestureScope.DONT_EXPECT_PILFER); + sendPointer(clickTime, clickTime, MotionEvent.ACTION_UP, center, + GestureScope.DONT_EXPECT_PILFER); } void clickLauncherObject(UiObject2 object) { - clickObject(object, GestureScope.INSIDE); + clickObject(object); } void scrollToLastVisibleRow( @@ -1582,7 +1569,8 @@ public final class LauncherInstrumentation { executeAndWaitForLauncherEvent( () -> linearGesture( - startX, startY, endX, endY, steps, slowDown, GestureScope.INSIDE), + startX, startY, endX, endY, steps, slowDown, + GestureScope.DONT_EXPECT_PILFER), event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()), () -> "Didn't receive a scroll end message: " + startX + ", " + startY + ", " + endX + ", " + endY, @@ -1592,18 +1580,43 @@ public final class LauncherInstrumentation { // Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a // fixed interval each time. public void linearGesture(int startX, int startY, int endX, int endY, int steps, - boolean slowDown, - GestureScope gestureScope) { + boolean slowDown, GestureScope gestureScope) { log("linearGesture: " + startX + ", " + startY + " -> " + endX + ", " + endY); final long downTime = SystemClock.uptimeMillis(); final Point start = new Point(startX, startY); final Point end = new Point(endX, endY); sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope); + if (mTrackpadGestureType != TrackpadGestureType.NONE) { + sendPointer(downTime, downTime, getPointerAction(MotionEvent.ACTION_POINTER_DOWN, 1), + start, gestureScope); + if (mTrackpadGestureType == TrackpadGestureType.THREE_FINGER + || mTrackpadGestureType == TrackpadGestureType.FOUR_FINGER) { + sendPointer(downTime, downTime, + getPointerAction(MotionEvent.ACTION_POINTER_DOWN, 2), + start, gestureScope); + if (mTrackpadGestureType == TrackpadGestureType.FOUR_FINGER) { + sendPointer(downTime, downTime, + getPointerAction(MotionEvent.ACTION_POINTER_DOWN, 3), + start, gestureScope); + } + } + } final long endTime = movePointer( start, end, steps, false, downTime, downTime, slowDown, gestureScope); + if (mTrackpadGestureType != TrackpadGestureType.NONE) { + for (int i = mPointerCount; i >= 2; i--) { + sendPointer(downTime, downTime, + getPointerAction(MotionEvent.ACTION_POINTER_UP, i - 1), + start, gestureScope); + } + } sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end, gestureScope); } + private static int getPointerAction(int action, int index) { + return action + (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT); + } + long movePointer(Point start, Point end, int steps, boolean isDecelerating, long downTime, long startTime, boolean slowDown, GestureScope gestureScope) { long endTime = movePointer(downTime, startTime, steps * GESTURE_STEP_MS, @@ -1627,74 +1640,104 @@ public final class LauncherInstrumentation { return getContext().getResources(); } + private static MotionEvent getTrackpadMotionEvent(long downTime, long eventTime, + int action, float x, float y, int pointerCount, TrackpadGestureType gestureType) { + MotionEvent.PointerProperties[] pointerProperties = + new MotionEvent.PointerProperties[pointerCount]; + MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[pointerCount]; + boolean isMultiFingerGesture = gestureType != TrackpadGestureType.TWO_FINGER; + for (int i = 0; i < pointerCount; i++) { + pointerProperties[i] = getPointerProperties(i); + pointerCoords[i] = getPointerCoords(x, y); + if (isMultiFingerGesture) { + pointerCoords[i].setAxisValue(AXIS_GESTURE_SWIPE_FINGER_COUNT, + gestureType == TrackpadGestureType.THREE_FINGER ? 3 : 4); + } + } + return MotionEvent.obtain(downTime, eventTime, action, pointerCount, pointerProperties, + pointerCoords, 0, 0, 1.0f, 1.0f, 0, 0, + InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_CLASS_POINTER, 0, 0, + isMultiFingerGesture ? MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE + : MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE); + } + private static MotionEvent getMotionEvent(long downTime, long eventTime, int action, - float x, float y) { + float x, float y, int source) { + return MotionEvent.obtain(downTime, eventTime, action, 1, + new MotionEvent.PointerProperties[]{getPointerProperties(0)}, + new MotionEvent.PointerCoords[]{getPointerCoords(x, y)}, + 0, 0, 1.0f, 1.0f, 0, 0, source, 0); + } + + private static MotionEvent.PointerProperties getPointerProperties(int pointerId) { MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties(); - properties.id = 0; + properties.id = pointerId; properties.toolType = Configurator.getInstance().getToolType(); + return properties; + } + private static MotionEvent.PointerCoords getPointerCoords(float x, float y) { MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords(); coords.pressure = 1; coords.size = 1; coords.x = x; coords.y = y; - - return MotionEvent.obtain(downTime, eventTime, action, 1, - new MotionEvent.PointerProperties[]{properties}, - new MotionEvent.PointerCoords[]{coords}, - 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0); + return coords; } private boolean hasTIS() { - return getTestInfo(TestProtocol.REQUEST_HAS_TIS).getBoolean(TestProtocol.REQUEST_HAS_TIS); + return getTestInfo(TestProtocol.REQUEST_HAS_TIS).getBoolean( + TestProtocol.TEST_INFO_RESPONSE_FIELD); } + boolean isGridOnlyOverviewEnabled() { + return getTestInfo(TestProtocol.REQUEST_FLAG_ENABLE_GRID_ONLY_OVERVIEW).getBoolean( + TestProtocol.TEST_INFO_RESPONSE_FIELD); + } public void sendPointer(long downTime, long currentTime, int action, Point point, GestureScope gestureScope) { + sendPointer(downTime, currentTime, action, point, gestureScope, + InputDevice.SOURCE_TOUCHSCREEN); + } + + public void sendPointer(long downTime, long currentTime, int action, Point point, + GestureScope gestureScope, int source) { final boolean hasTIS = hasTIS(); - switch (action) { + int pointerCount = mPointerCount; + + boolean isTrackpadGesture = mTrackpadGestureType != TrackpadGestureType.NONE; + switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: - if (gestureScope != GestureScope.OUTSIDE_WITH_PILFER - && gestureScope != GestureScope.OUTSIDE_WITHOUT_PILFER - && gestureScope != GestureScope.OUTSIDE_WITH_KEYCODE) { - expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN); - } - if (hasTIS && getNavigationModel() != NavigationModel.THREE_BUTTON) { - expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS); + if (isTrackpadGesture) { + mPointerCount = 1; + pointerCount = mPointerCount; } break; case MotionEvent.ACTION_UP: - if (hasTIS && gestureScope != GestureScope.INSIDE - && gestureScope != GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER - && (gestureScope == GestureScope.OUTSIDE_WITH_PILFER - || gestureScope == GestureScope.INSIDE_TO_OUTSIDE)) { + if (hasTIS && gestureScope == GestureScope.EXPECT_PILFER) { expectEvent(TestProtocol.SEQUENCE_PILFER, EVENT_PILFER_POINTERS); } - if (gestureScope != GestureScope.OUTSIDE_WITH_PILFER - && gestureScope != GestureScope.OUTSIDE_WITHOUT_PILFER - && gestureScope != GestureScope.OUTSIDE_WITH_KEYCODE) { - expectEvent(TestProtocol.SEQUENCE_MAIN, - gestureScope == GestureScope.INSIDE - || gestureScope == GestureScope.OUTSIDE_WITHOUT_PILFER - ? EVENT_TOUCH_UP : EVENT_TOUCH_CANCEL); - } - if (hasTIS && getNavigationModel() != NavigationModel.THREE_BUTTON) { - expectEvent(TestProtocol.SEQUENCE_TIS, - gestureScope == GestureScope.INSIDE_TO_OUTSIDE_WITH_KEYCODE - || gestureScope == GestureScope.OUTSIDE_WITH_KEYCODE - ? EVENT_TOUCH_CANCEL_TIS : EVENT_TOUCH_UP_TIS); - } break; - case MotionEvent.ACTION_HOVER_ENTER: - expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_HOVER_ENTER_TIS); + case MotionEvent.ACTION_POINTER_DOWN: + mPointerCount++; + pointerCount = mPointerCount; break; - case MotionEvent.ACTION_HOVER_EXIT: - expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_HOVER_EXIT_TIS); + case MotionEvent.ACTION_POINTER_UP: + // When the gesture is handled outside, it's cancelled within launcher. + mPointerCount--; break; } - final MotionEvent event = getMotionEvent(downTime, currentTime, action, point.x, point.y); + final MotionEvent event = isTrackpadGesture + ? getTrackpadMotionEvent( + downTime, currentTime, action, point.x, point.y, pointerCount, + mTrackpadGestureType) + : getMotionEvent(downTime, currentTime, action, point.x, point.y, source); + if (action == MotionEvent.ACTION_BUTTON_PRESS + || action == MotionEvent.ACTION_BUTTON_RELEASE) { + event.setActionButton(MotionEvent.BUTTON_PRIMARY); + } assertTrue("injectInputEvent failed", mInstrumentation.getUiAutomation().injectInputEvent(event, true, false)); event.recycle(); @@ -1702,8 +1745,7 @@ public final class LauncherInstrumentation { public long movePointer(long downTime, long startTime, long duration, Point from, Point to, GestureScope gestureScope) { - return movePointer( - downTime, startTime, duration, false, from, to, gestureScope); + return movePointer(downTime, startTime, duration, false, from, to, gestureScope); } public long movePointer(long downTime, long startTime, long duration, boolean isDecelerating, @@ -1763,11 +1805,12 @@ public final class LauncherInstrumentation { @NonNull final UiObject2 target, @NonNull String resName, Pattern longClickEvent) { final Point targetCenter = target.getVisibleCenter(); final long downTime = SystemClock.uptimeMillis(); - sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetCenter, GestureScope.INSIDE); + sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetCenter, + GestureScope.DONT_EXPECT_PILFER); expectEvent(TestProtocol.SEQUENCE_MAIN, longClickEvent); final UiObject2 result = waitForLauncherObject(resName); sendPointer(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, targetCenter, - GestureScope.INSIDE); + GestureScope.DONT_EXPECT_PILFER); return result; } @@ -1851,6 +1894,12 @@ public final class LauncherInstrumentation { return tasks; } + /** Reinitializes the workspace to its default layout. */ + public void reinitializeLauncherData() { + getTestInfo(TestProtocol.REQUEST_REINITIALIZE_DATA); + } + + /** Clears the workspace, leaving it empty. */ public void clearLauncherData() { getTestInfo(TestProtocol.REQUEST_CLEAR_DATA); } @@ -2042,25 +2091,9 @@ public final class LauncherInstrumentation { final long downTime = SystemClock.uptimeMillis(); final Point tapTarget = new Point(x, y); sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, tapTarget, - LauncherInstrumentation.GestureScope.INSIDE); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); sendPointer(downTime, downTime, MotionEvent.ACTION_UP, tapTarget, - LauncherInstrumentation.GestureScope.INSIDE); - } - } - - /** - * Sets the consumer to run callbacks at all run-points. - */ - public void setRunPointCallback(Consumer<CALLBACK_RUN_POINT> callback) { - mCallbackAtRunPoint = callback; - } - - /** - * Runs the callback at the specified point if it exists. - */ - void runCallbackIfActive(CALLBACK_RUN_POINT runPoint) { - if (mCallbackAtRunPoint != null) { - mCallbackAtRunPoint.accept(runPoint); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); } } diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java index 386deac579..bd2c9c1bd4 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java @@ -19,8 +19,6 @@ package com.android.launcher3.tapl; import androidx.annotation.NonNull; import androidx.test.uiautomator.UiObject2; -import com.android.launcher3.testing.shared.TestProtocol; - /** * View containing overview actions */ @@ -51,13 +49,6 @@ public class OverviewActions { "clicked screenshot button")) { UiObject2 closeScreenshot = mLauncher.waitForSystemUiObject( "screenshot_dismiss_image"); - if (mLauncher.getNavigationModel() - != LauncherInstrumentation.NavigationModel.THREE_BUTTON) { - mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS, - LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS); - mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS, - LauncherInstrumentation.EVENT_TOUCH_UP_TIS); - } closeScreenshot.click(); try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer( "dismissed screenshot")) { diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java index 39b93b4b59..e4cfc52a2c 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java @@ -124,7 +124,7 @@ public final class OverviewTask { final int centerY = taskBounds.centerY(); mLauncher.executeAndWaitForLauncherEvent( () -> mLauncher.linearGesture(centerX, centerY, centerX, 0, 10, false, - LauncherInstrumentation.GestureScope.INSIDE), + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER), event -> TestProtocol.DISMISS_ANIMATION_ENDS_MESSAGE.equals(event.getClassName()), () -> "Didn't receive a dismiss animation ends message: " + centerX + ", " + centerY, "swiping to dismiss"); diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java index e349620e28..7c29a6cf87 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java @@ -50,6 +50,24 @@ public class OverviewTaskMenu { } } + /** Taps the app info item from the overview task menu and returns the LaunchedAppState + * representing the App info settings page. */ + @NonNull + public LaunchedAppState tapAppInfoMenuItem() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "before tapping the app info menu item")) { + mLauncher.clickLauncherObject( + mLauncher.findObjectInContainer(mMenu, By.text("App info"))); + + try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( + "tapped app info menu item")) { + mLauncher.waitUntilSystemLauncherObjectGone("overview_panel"); + return new LaunchedAppState(mLauncher); + } + } + } + /** Returns true if an item matching the given string is present in the menu. */ public boolean hasMenuItem(String expectedMenuItemText) { UiObject2 menuItem = mLauncher.findObjectInContainer(mMenu, By.text(expectedMenuItemText)); diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenuItem.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenuItem.java index b2cc92de09..e3035bf71b 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenuItem.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenuItem.java @@ -15,13 +15,7 @@ */ package com.android.launcher3.tapl; -import static com.android.launcher3.tapl.LauncherInstrumentation.CALLBACK_RUN_POINT.CALLBACK_HOVER_ENTER; -import static com.android.launcher3.tapl.LauncherInstrumentation.CALLBACK_RUN_POINT.CALLBACK_HOVER_EXIT; - -import android.graphics.Point; import android.graphics.Rect; -import android.os.SystemClock; -import android.view.MotionEvent; import androidx.test.uiautomator.UiObject2; @@ -42,28 +36,4 @@ public class OverviewTaskMenuItem { public Rect getVisibleBounds() { return mMenuItem.getVisibleBounds(); } - - /** - * Emulate the cursor entering and exiting a hover over this menu item. - */ - public void hoverCursor() { - try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); - LauncherInstrumentation.Closable c = mLauncher.addContextLayer( - "cursor hover entering menu item")) { - long downTime = SystemClock.uptimeMillis(); - mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER, - new Point(mMenuItem.getVisibleCenter().x, mMenuItem.getVisibleCenter().y), - null); - mLauncher.runCallbackIfActive(CALLBACK_HOVER_ENTER); - - try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( - "cursor hover exiting menu item")) { - downTime = SystemClock.uptimeMillis(); - mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT, - new Point(mMenuItem.getVisibleCenter().x, mMenuItem.getVisibleCenter().y), - null); - mLauncher.runCallbackIfActive(CALLBACK_HOVER_EXIT); - } - } - } } diff --git a/tests/tapl/com/android/launcher3/tapl/Qsb.java b/tests/tapl/com/android/launcher3/tapl/Qsb.java index 6bc4f2109c..7f3f61d81f 100644 --- a/tests/tapl/com/android/launcher3/tapl/Qsb.java +++ b/tests/tapl/com/android/launcher3/tapl/Qsb.java @@ -30,13 +30,21 @@ public abstract class Qsb { private static final String ASSISTANT_APP_PACKAGE = "com.google.android.googlequicksearchbox"; private static final String ASSISTANT_ICON_RES_ID = "mic_icon"; protected final LauncherInstrumentation mLauncher; + private final UiObject2 mContainer; + private final String mQsbResName; - protected Qsb(LauncherInstrumentation launcher) { + protected Qsb(LauncherInstrumentation launcher, UiObject2 container, String qsbResName) { mLauncher = launcher; + mContainer = container; + mQsbResName = qsbResName; + waitForQsbObject(); } // Waits for the quick search box. - protected abstract UiObject2 waitForQsbObject(); + private UiObject2 waitForQsbObject() { + return mLauncher.waitForObjectInContainer(mContainer, mQsbResName); + } + /** * Launch assistant app by tapping mic icon on qsb. */ @@ -79,8 +87,12 @@ public abstract class Qsb { mLauncher.waitForIdle(); try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer( "clicked qsb to open search result page")) { - return new SearchResultFromQsb(mLauncher); + return createSearchResult(); } } } + + protected SearchResultFromQsb createSearchResult() { + return new SearchResultFromQsb(mLauncher); + } } diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java index 80176e993f..d02e747900 100644 --- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java +++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java @@ -32,7 +32,7 @@ public class SearchResultFromQsb { // This particular ID change should happen with caution private static final String SEARCH_CONTAINER_RES_ID = "search_results_list_view"; - private final LauncherInstrumentation mLauncher; + protected final LauncherInstrumentation mLauncher; SearchResultFromQsb(LauncherInstrumentation launcher) { mLauncher = launcher; @@ -49,27 +49,33 @@ public class SearchResultFromQsb { } /** Find the app from search results with app name. */ - public Launchable findAppIcon(String appName) { + public AppIcon findAppIcon(String appName) { UiObject2 icon = mLauncher.waitForLauncherObject(By.clazz(TextView.class).text(appName)); + return createAppIcon(icon); + } + + protected AppIcon createAppIcon(UiObject2 icon) { return new AllAppsAppIcon(mLauncher, icon); } /** Find the web suggestion from search suggestion's title text */ - public void verifyWebSuggestIsPresent(String text) { - ArrayList<UiObject2> goldenGateResults = + public SearchWebSuggestion findWebSuggestion(String text) { + ArrayList<UiObject2> webSuggestions = new ArrayList<>(mLauncher.waitForObjectsInContainer( mLauncher.waitForSystemLauncherObject(SEARCH_CONTAINER_RES_ID), By.clazz(TextView.class))); - boolean found = false; - for(UiObject2 uiObject: goldenGateResults) { + for (UiObject2 uiObject: webSuggestions) { String currentString = uiObject.getText(); if (currentString.equals(text)) { - found = true; + return createWebSuggestion(uiObject); } } - if (!found) { - throw new IllegalStateException("Web suggestion title: " + text + " not found"); - } + mLauncher.fail("Web suggestion title: " + text + " not found"); + return null; + } + + protected SearchWebSuggestion createWebSuggestion(UiObject2 webSuggestion) { + return new SearchWebSuggestion(mLauncher, webSuggestion); } /** Find the total amount of views being displayed and return the size */ diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java new file mode 100644 index 0000000000..6c6ab05ac3 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.tapl; + +import androidx.test.uiautomator.UiObject2; + +/** + * Operations on search result page opened from Taskbar qsb. + */ +public class SearchResultFromTaskbarQsb extends SearchResultFromQsb { + + SearchResultFromTaskbarQsb(LauncherInstrumentation launcher) { + super(launcher); + } + + @Override + public TaskbarAppIcon findAppIcon(String appName) { + return (TaskbarAppIcon) super.findAppIcon(appName); + } + + @Override + protected TaskbarAppIcon createAppIcon(UiObject2 icon) { + return new TaskbarAppIcon(mLauncher, icon); + } + + @Override + public TaskbarSearchWebSuggestion findWebSuggestion(String text) { + return (TaskbarSearchWebSuggestion) super.findWebSuggestion(text); + } + + @Override + protected TaskbarSearchWebSuggestion createWebSuggestion(UiObject2 webSuggestion) { + return new TaskbarSearchWebSuggestion(mLauncher, webSuggestion); + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/SearchWebSuggestion.java b/tests/tapl/com/android/launcher3/tapl/SearchWebSuggestion.java new file mode 100644 index 0000000000..e4dec98a71 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/SearchWebSuggestion.java @@ -0,0 +1,57 @@ +/* + * 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.launcher3.tapl; + +import androidx.test.uiautomator.UiObject2; + +import com.android.launcher3.testing.shared.TestProtocol; + +import java.util.regex.Pattern; + +/** + * Operations on a search web suggestion from a qsb. + */ +public class SearchWebSuggestion extends Launchable { + + private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onAllAppsItemLongClick"); + + SearchWebSuggestion(LauncherInstrumentation launcher, UiObject2 object) { + super(launcher, object); + } + + @Override + protected void expectActivityStartEvents() { + } + + @Override + protected String launchableType() { + return "search web suggestion"; + } + + @Override + protected void waitForLongPressConfirmation() { + mLauncher.waitForLauncherObject("popup_container"); + } + + @Override + protected void addExpectedEventsForLongClick() { + mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, getLongClickEvent()); + } + + protected Pattern getLongClickEvent() { + return LONG_CLICK_EVENT; + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java index 051630e185..8671738fe7 100644 --- a/tests/tapl/com/android/launcher3/tapl/Taskbar.java +++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java @@ -76,13 +76,13 @@ public final class Taskbar { mLauncher.getRealDisplaySize().x - 1, mLauncher.getRealDisplaySize().y - 1); mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, stashTarget, - LauncherInstrumentation.GestureScope.INSIDE); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); LauncherInstrumentation.log("hideTaskbar: sent down"); try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) { mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID); mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, stashTarget, - LauncherInstrumentation.GestureScope.INSIDE); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); } } finally { mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING); @@ -147,9 +147,9 @@ public final class Taskbar { mLauncher.getRealDisplaySize().y - 1); mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, tapTarget, - LauncherInstrumentation.GestureScope.INSIDE); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, tapTarget, - LauncherInstrumentation.GestureScope.INSIDE); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); } } } diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAllAppsQsb.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAllAppsQsb.java new file mode 100644 index 0000000000..7cecd3e553 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAllAppsQsb.java @@ -0,0 +1,38 @@ +/* + * 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.launcher3.tapl; + +import androidx.test.uiautomator.UiObject2; + +/** + * Operations on Taskbar AllApp screen qsb. + */ +public class TaskbarAllAppsQsb extends Qsb { + + TaskbarAllAppsQsb(LauncherInstrumentation launcher, UiObject2 allAppsContainer) { + super(launcher, allAppsContainer, "search_container_all_apps"); + } + + @Override + public SearchResultFromTaskbarQsb showSearchResult() { + return (SearchResultFromTaskbarQsb) super.showSearchResult(); + } + + @Override + protected SearchResultFromTaskbarQsb createSearchResult() { + return new SearchResultFromTaskbarQsb(mLauncher); + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarSearchWebSuggestion.java b/tests/tapl/com/android/launcher3/tapl/TaskbarSearchWebSuggestion.java new file mode 100644 index 0000000000..cd8ce42f49 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/TaskbarSearchWebSuggestion.java @@ -0,0 +1,45 @@ +/* + * 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.launcher3.tapl; + +import androidx.test.uiautomator.UiObject2; + +import java.util.regex.Pattern; + +/** + * Operations on a search web suggestion from the Taskbar qsb. + */ +public class TaskbarSearchWebSuggestion extends SearchWebSuggestion implements + SplitscreenDragSource { + + private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onTaskbarItemLongClick"); + + TaskbarSearchWebSuggestion(LauncherInstrumentation launcher, + UiObject2 object) { + super(launcher, object); + } + + @Override + protected Pattern getLongClickEvent() { + return LONG_CLICK_EVENT; + } + + /** This method requires public access, however should not be called in tests. */ + @Override + public Launchable getLaunchable() { + return this; + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 7bb02cb518..6f6f2926b7 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -18,7 +18,6 @@ package com.android.launcher3.tapl; import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_SCROLLED; -import static com.android.launcher3.tapl.LauncherInstrumentation.CALLBACK_RUN_POINT.CALLBACK_HOLD_BEFORE_DROP; import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL; import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL; @@ -105,7 +104,8 @@ public final class Workspace extends Home { windowCornerRadius, startY - swipeHeight - mLauncher.getTouchSlop(), 12, - ALL_APPS_STATE_ORDINAL, LauncherInstrumentation.GestureScope.INSIDE); + ALL_APPS_STATE_ORDINAL, + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( "swiped to all apps")) { @@ -387,7 +387,7 @@ public final class Workspace extends Home { Until.hasObject(installerAlert), LauncherInstrumentation.WAIT_TIME_MS)); final UiObject2 ok = device.findObject(By.text("OK")); assertNotNull("OK button is not shown", ok); - launcher.clickObject(ok, LauncherInstrumentation.GestureScope.OUTSIDE_WITHOUT_PILFER); + launcher.clickObject(ok); assertTrue("Uninstall alert is not dismissed after clicking OK", device.wait( Until.gone(installerAlert), LauncherInstrumentation.WAIT_TIME_MS)); @@ -454,7 +454,7 @@ public final class Workspace extends Home { launcher.runToState( () -> launcher.sendPointer( downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest, - LauncherInstrumentation.GestureScope.INSIDE), + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER), NORMAL_STATE_ORDINAL, "sending UP event"); if (expectedEvents != null) { @@ -543,7 +543,7 @@ public final class Workspace extends Home { executeAndWaitForPageScroll(launcher, () -> launcher.movePointer(finalDragStart, screenEdge, DEFAULT_DRAG_STEPS, true, downTime, downTime, true, - LauncherInstrumentation.GestureScope.INSIDE)); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER)); targetDest.x += displayX * (targetDest.x > 0 ? -1 : 1); dragStart = screenEdge; } @@ -552,8 +552,7 @@ public final class Workspace extends Home { // we just have to put move the icon to the destination and drop it launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating, downTime, SystemClock.uptimeMillis(), false, - LauncherInstrumentation.GestureScope.INSIDE); - launcher.runCallbackIfActive(CALLBACK_HOLD_BEFORE_DROP); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents); } } @@ -586,8 +585,7 @@ public final class Workspace extends Home { // we just have to put move the icon to the destination and drop it launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating, downTime, SystemClock.uptimeMillis(), false, - LauncherInstrumentation.GestureScope.INSIDE); - launcher.runCallbackIfActive(CALLBACK_HOLD_BEFORE_DROP); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents); } } @@ -624,7 +622,7 @@ public final class Workspace extends Home { executeAndWaitForPageScroll(launcher, () -> launcher.movePointer(finalDragStart, screenEdge, DEFAULT_DRAG_STEPS, true, downTime, downTime, true, - LauncherInstrumentation.GestureScope.INSIDE)); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER)); currentPage = Workspace.geCurrentPage(launcher); currentPosition = screenEdge; } @@ -653,7 +651,7 @@ public final class Workspace extends Home { launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, true, downTime, SystemClock.uptimeMillis(), false, - LauncherInstrumentation.GestureScope.INSIDE); + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents); } |