diff options
264 files changed, 5007 insertions, 2237 deletions
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig index 760d8ac6d6..af175ce921 100644 --- a/aconfig/launcher.aconfig +++ b/aconfig/launcher.aconfig @@ -76,3 +76,17 @@ flag { description: "Enables logging of Launcher restore metrics to the Backup & Restore team" bug: "307527314" } + +flag { + name: "enable_unfolded_two_pane_picker" + namespace: "launcher" + description: "Enables two pane widget picker for unfolded foldables" + bug: "313922374" +} + +flag { + name: "enable_tablet_two_pane_picker_v2" + namespace: "launcher" + description: "Enables full width two pane widget picker for tablets in landscape and portrait" + bug: "315055849" +} diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto index 5a9e1474b5..7c648b6d88 100644 --- a/protos/launcher_atom.proto +++ b/protos/launcher_atom.proto @@ -183,6 +183,7 @@ enum Attribute { ALL_APPS_SEARCH_RESULT_CHROMETAB = 24; ALL_APPS_SEARCH_RESULT_NAVVYSITE = 25 [deprecated = true]; ALL_APPS_SEARCH_RESULT_TIPS = 26; + ALL_APPS_SEARCH_RESULT_QS_TILE = 27; ALL_APPS_SEARCH_RESULT_PEOPLE_TILE = 27 [deprecated = true]; ALL_APPS_SEARCH_RESULT_LEGACY_SHORTCUT = 30; ALL_APPS_SEARCH_RESULT_ASSISTANT_MEMORY = 31; @@ -192,7 +193,6 @@ enum Attribute { ALL_APPS_SEARCH_RESULT_LOCATION = 50; ALL_APPS_SEARCH_RESULT_TEXT_HEADER = 51; ALL_APPS_SEARCH_RESULT_NO_FULFILLMENT = 52; - ALL_APPS_SEARCH_RESULT_QS_TILE = 53; // Result sources DATA_SOURCE_APPSEARCH_APP_PREVIEW = 45; @@ -200,6 +200,7 @@ enum Attribute { DATA_SOURCE_APPSEARCH_CATEGORY_SRP_PREVIEW = 48; DATA_SOURCE_APPSEARCH_ENTITY_SRP_PREVIEW = 49; DATA_SOURCE_AIAI_SEARCH_ROOT = 47; + DATA_SOURCE_LAUNCHER = 53; // Web suggestions provided by AGA ALL_APPS_SEARCH_RESULT_WEB_SUGGEST = 39; diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml index 82f8ebeaec..db46508385 100644 --- a/quickstep/AndroidManifest.xml +++ b/quickstep/AndroidManifest.xml @@ -43,6 +43,10 @@ <uses-permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY" /> + <!-- Permission required to start a WidgetPickerActivity. --> + <permission android:name="${packageName}.permission.START_WIDGET_PICKER_ACTIVITY" + android:protectionLevel="signature|privileged" /> + <application android:backupAgent="com.android.launcher3.LauncherBackupAgent" android:fullBackupOnly="true" android:fullBackupContent="@xml/backupscheme" @@ -133,6 +137,20 @@ </intent-filter> </activity> + <activity android:name="com.android.launcher3.WidgetPickerActivity" + android:theme="@style/WidgetPickerActivityTheme" + android:excludeFromRecents="true" + android:autoRemoveFromRecents="true" + android:showOnLockScreen="true" + android:launchMode="singleTop" + android:exported="true" + android:permission="android.permission.START_WIDGET_PICKER_ACTIVITY"> + <intent-filter> + <action android:name="android.intent.action.PICK" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + </application> </manifest> diff --git a/quickstep/res/layout/widget_picker_activity.xml b/quickstep/res/layout/widget_picker_activity.xml new file mode 100644 index 0000000000..3388e40f0c --- /dev/null +++ b/quickstep/res/layout/widget_picker_activity.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. + --> + +<com.android.launcher3.dragndrop.SimpleDragLayer + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/drag_layer" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:clipChildren="false" + android:clipToPadding="false" + android:importantForAccessibility="no" /> diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml index 75c6c6063d..a6da8721ae 100644 --- a/quickstep/res/values-af/strings.xml +++ b/quickstep/res/values-af/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Skermkiekie"</string> <string name="action_split" msgid="2098009717623550676">"Verdeel"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Tik op ’n ander app om verdeelde skerm te gebruik"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Kanselleer"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Verlaat verdeeldeskermkeuse"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Kies nog ’n app as jy verdeelde skerm wil gebruik"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Jou organisasie laat nie hierdie program toe nie"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Sleep ’n app na die kant toe om 2 apps tegelyk te gebruik"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Stadigswiep op om die Taakbalk te wys"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Kry appvoorstelle op grond van jou roetine"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Skakel gebaarnavigasie in Instellings aan om die Taakbalk outomaties te versteek"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Langdruk op die verdeler om die Taakbalk vas te speld"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Doen meer met die Taakbalk"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Maak toe"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Klaar"</string> diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml index 2c39370720..a825e3e700 100644 --- a/quickstep/res/values-am/strings.xml +++ b/quickstep/res/values-am/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"ቅጽበታዊ ገፅ ዕይታ"</string> <string name="action_split" msgid="2098009717623550676">"ክፈል"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"የተከፈለ ማያ ገጽን ለመጠቀም ሌላ መተግበሪያ መታ ያድርጉ"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"ይቅር"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"ከተከፈለ ማያ ገፅ ምርጫ ይውጡ"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"የተከፈለ ማያ ገጽን ለመቀበል ሌላ መተግበሪያ ይምረጡ"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ይህ ድርጊት በመተግበሪያው ወይም በእርስዎ ድርጅት አይፈቀድም"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"የተግባር አሞሌውን ፒን ለማድረግ በአከፋፋዩ ላይ በረጅሙ ይጫኑ"</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> diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml index 12f03638c5..09f146c614 100644 --- a/quickstep/res/values-ar/strings.xml +++ b/quickstep/res/values-ar/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"لقطة شاشة"</string> <string name="action_split" msgid="2098009717623550676">"تقسيم"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"انقر على تطبيق آخر لاستخدام وضع تقسيم الشاشة."</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"إلغاء"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"الخروج من وضع تقسيم الشاشة"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"اختَر تطبيقًا آخر لاستخدام وضع تقسيم الشاشة."</string> <string name="blocked_by_policy" msgid="2071401072261365546">"لا يسمح التطبيق أو لا تسمح مؤسستك بهذا الإجراء."</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"اضغط مع الاستمرار على المقسِّم لتثبيت \"شريط التطبيقات\""</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> diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml index 65c0a10cf7..ea95d2ca6f 100644 --- a/quickstep/res/values-as/strings.xml +++ b/quickstep/res/values-as/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"স্ক্ৰীনশ্বট"</string> <string name="action_split" msgid="2098009717623550676">"বিভাজন কৰক"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ অন্য এটা এপত টিপক"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"বাতিল কৰক"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"বিভাজিত স্ক্ৰীনৰ বাছনিৰ পৰা বাহিৰ হওক"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ অন্য এটা এপ্ বাছক"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"এপ্টোৱে অথবা আপোনাৰ প্ৰতিষ্ঠানে এই কাৰ্যটোৰ অনুমতি নিদিয়ে"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"টাস্কবাৰ পিন কৰিবলৈ বিভাজকত দীঘলীয়া সময় টিপি থাকক"</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> diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml index 01d61eeb6c..510749c4cf 100644 --- a/quickstep/res/values-az/strings.xml +++ b/quickstep/res/values-az/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Skrinşot"</string> <string name="action_split" msgid="2098009717623550676">"Ayırın"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Bölünmüş ekran üçün başqa tətbiqə toxunun"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Ləğv edin"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Bölünmüş ekran seçimindən çıxın"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Bölünmüş ekrandan istifadə üçün başqa tətbiq seçin"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Bu əməliyyata tətbiq və ya təşkilatınız tərəfindən icazə verilmir"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Eyni anda 2 tətbiqi istifadə etmək üçün bir tətbiqi yan tərəfə çəkin"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Tapşırıq panelini göstərmək üçün astaca yuxarı sürüşdürün"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Rejiminizə əsasən tətbiq təklifləri alın"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Tapşırıq panelini avtomatik gizlətmək üçün Ayarlarda jest naviqasiyasını aktiv edin"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Ayırıcı üzərinə basıb saxlayaraq İşləmə panelini bərkidin"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Tapşırıq paneli ilə daha çox şey edin"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Bağlayın"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Hazırdır"</string> diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml index 805fc7f255..79e3616b19 100644 --- a/quickstep/res/values-b+sr+Latn/strings.xml +++ b/quickstep/res/values-b+sr+Latn/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Snimak ekrana"</string> <string name="action_split" msgid="2098009717623550676">"Podeli"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Dodirnite drugu aplikaciju za podeljeni ekran"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Otkaži"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Izlazak iz biranja podeljenog ekrana"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Odaberite drugu aplikaciju za podeljeni ekran"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ili organizacija ne dozvoljavaju ovu radnju"</string> @@ -110,7 +109,7 @@ <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_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_pinning" msgid="6708550858580071558">"Dugo pritiskajte razdelnik da biste zakačili traku zadataka"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Uradite više pomoću trake zadataka"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Zatvori"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Gotovo"</string> diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml index 1cc8c35765..c164f95f46 100644 --- a/quickstep/res/values-be/strings.xml +++ b/quickstep/res/values-be/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Здымак экрана"</string> <string name="action_split" msgid="2098009717623550676">"Падзелены экран"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Каб падзяліць экран, націсніце на іншую праграму"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Скасаваць"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Выйсці з рэжыму падзеленага экрана"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Каб падзяліць экран, выберыце іншую праграму"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Гэта дзеянне не дазволена праграмай ці вашай арганізацыяй"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Замацуйце панэль задач доўгім націсканнем на раздзяляльнік"</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> diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml index b0889df8a8..923908911c 100644 --- a/quickstep/res/values-bg/strings.xml +++ b/quickstep/res/values-bg/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Екранна снимка"</string> <string name="action_split" msgid="2098009717623550676">"Разделяне на екрана"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Докоснете друго прил., за да ползвате разд. екран"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Отказ"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Изход от избора на разделен екран"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"За разделен екран изберете още едно приложение"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Това действие не е разрешено от приложението или организацията ви"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Натиснете продължително разделителя, за да фиксирате лентата на задачите"</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> diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml index abc9603765..80d0275956 100644 --- a/quickstep/res/values-bn/strings.xml +++ b/quickstep/res/values-bn/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"স্ক্রিনশট নিন"</string> <string name="action_split" msgid="2098009717623550676">"স্প্লিট"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"স্প্লিট স্ক্রিন ব্যবহারের জন্য অ্যাপে ট্যাপ করুন"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"বাতিল করুন"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"স্প্লিট স্ক্রিন বেছে নেওয়ার বিকল্প থেকে বেরিয়ে আসুন"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"স্প্লিট স্ক্রিন ব্যবহার করতে অন্য অ্যাপ বেছে নিন"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"এই অ্যাপ বা আপনার প্রতিষ্ঠান এই অ্যাকশনটি পারফর্ম করার অনুমতি দেয়নি"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"টাস্কবার পিন করতে, ড্রাইভার বেশ কিছুক্ষণ প্রেস করে রাখুন"</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> diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml index 2e4e442c87..e22e8567f1 100644 --- a/quickstep/res/values-bs/strings.xml +++ b/quickstep/res/values-bs/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Snimak ekrana"</string> <string name="action_split" msgid="2098009717623550676">"Podijeli"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Dodirnite drugu apl. da koristite podijeljeni ekran"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Otkaži"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Izlaz iz odabira podijeljenog ekrana"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Odaberite drugu apl. da koristite podijeljeni ekran"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Ovu radnju ne dozvoljava aplikacija ili vaša organizacija"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Prevucite aplikaciju ustranu da odjednom koristite 2 aplikacije"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Sporo prevucite nagore da vidite traku zadataka"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Dobijajte prijedloge aplikacija zasnovane na vašoj rutini"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Uključite navigaciju pokretima u Postavkama da automatski sakrijete traku zadataka"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Pritisnite i zadržite razdjelnik da zakačite traku zadataka"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Uradite više pomoću trake zadataka"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Zatvori"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Gotovo"</string> diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml index 0964a93aff..df8cb3679b 100644 --- a/quickstep/res/values-ca/strings.xml +++ b/quickstep/res/values-ca/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Captura de pantalla"</string> <string name="action_split" msgid="2098009717623550676">"Divideix"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Toca una altra app per utilitzar pantalla dividida"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Cancel·la"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Surt de la selecció de pantalla dividida"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Tria una altra app per utilitzar pantalla dividida"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"L\'aplicació o la teva organització no permeten aquesta acció"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Arrossega una aplicació al costat per utilitzar-ne dues 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_pinning" msgid="6708550858580071558">"Mantén premut el separador per fixar la Barra de tasques"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Treu més partit de la Barra de tasques"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Tanca"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Fet"</string> diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml index 6dd5526c50..1cd2ed9162 100644 --- a/quickstep/res/values-cs/strings.xml +++ b/quickstep/res/values-cs/strings.xml @@ -95,8 +95,7 @@ <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> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Zrušit"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Výběr opuštění rozdělené obrazovky"</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> @@ -110,7 +109,7 @@ <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_settings_persistent" msgid="1387372982791296151">"Pokud chcete panel aplikací automaticky skrýt, zapněte v Nastavení navigaci gesty"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Dlouhým stisknutím oddělovače připnete panel aplikací"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Více možností s panelem aplikací"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Zavřít"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Hotovo"</string> diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml index 9571fed502..d8f84dcdd9 100644 --- a/quickstep/res/values-da/strings.xml +++ b/quickstep/res/values-da/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string> <string name="action_split" msgid="2098009717623550676">"Opdel"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Tryk på en anden app for at bruge opdelt skærm"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Annuller"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Luk valg af 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> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Træk en app til siden for at bruge 2 apps på én gang"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Stryg langsomt opad for at se proceslinjen"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Få appforslag baseret på din rutine"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Aktivér navigation med bevægelser under Indstillinger for automatisk at skjule proceslinjen"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Fastgør proceslinjen med et langt tryk på skillelinjen"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Få mere fra hånden med proceslinjen"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Luk"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Luk"</string> diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml index 0c60f3a748..3d4ce43607 100644 --- a/quickstep/res/values-de/strings.xml +++ b/quickstep/res/values-de/strings.xml @@ -95,8 +95,7 @@ <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> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Abbrechen"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Splitscreen-Auswahl beenden"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Für Splitscreen andere App auswählen"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Die App oder deine Organisation lässt diese Aktion nicht zu"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"App zur Seite ziehen, um zwei Apps gleichzeitig zu nutzen"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Langsam nach oben wischen, um die Taskleiste anzuzeigen"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"App-Vorschläge auf Grundlage deiner Nutzung erhalten"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Taskleiste automatisch ausblenden: Aktiviere in „Einstellungen“ die Bedienung über Gesten."</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Bildschirmteiler lange drücken, um die Taskleiste anzupinnen"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Mehr Möglichkeiten mit der Taskleiste"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Schließen"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Fertig"</string> diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml index 103f55addd..335ebacb7d 100644 --- a/quickstep/res/values-el/strings.xml +++ b/quickstep/res/values-el/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Στιγμιότυπο οθόνης"</string> <string name="action_split" msgid="2098009717623550676">"Διαχωρισμός"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Πατήστε άλλη εφαρμογή για διαχωρισμό οθόνης"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Ακύρωση"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Έξοδος από την επιλογή διαχωρισμού οθόνης"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Επιλέξτε άλλη εφαρμογή για διαχωρισμό οθόνης"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Αυτή η ενέργεια δεν επιτρέπεται από την εφαρμογή ή τον οργανισμό σας."</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Παρατετ. πάτημα στο διαχωρ. για καρφ. της Γραμμής εργαλείων"</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> diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml index 5eb836b94a..04ec03f608 100644 --- a/quickstep/res/values-en-rAU/strings.xml +++ b/quickstep/res/values-en-rAU/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string> <string name="action_split" msgid="2098009717623550676">"Split"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Tap another app to use split screen"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Cancel"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Exit split screen selection"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Choose another app to use split screen"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Drag an app to the side to use two apps at once"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Slow-swipe up to show the Taskbar"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Get app suggestions based on your routine"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Turn on gesture navigation in Settings to auto-hide the Taskbar"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Long press on the divider to pin the Taskbar"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Do more with the Taskbar"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Close"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Done"</string> diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml index b7cc6f5492..5c8d0f2531 100644 --- a/quickstep/res/values-en-rCA/strings.xml +++ b/quickstep/res/values-en-rCA/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string> <string name="action_split" msgid="2098009717623550676">"Split"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Tap another app to use split screen"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Cancel"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Exit split screen selection"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Choose another app to use split screen"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organization"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Drag an app to the side to use 2 apps at once"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Slow-swipe up to show the Taskbar"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Get app suggestions based on your routine"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Turn on gesture navigation in Settings to auto-hide the Taskbar"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Long press on the divider to pin the Taskbar"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Do more with the Taskbar"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Close"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Done"</string> diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml index 5eb836b94a..04ec03f608 100644 --- a/quickstep/res/values-en-rGB/strings.xml +++ b/quickstep/res/values-en-rGB/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string> <string name="action_split" msgid="2098009717623550676">"Split"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Tap another app to use split screen"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Cancel"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Exit split screen selection"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Choose another app to use split screen"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Drag an app to the side to use two apps at once"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Slow-swipe up to show the Taskbar"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Get app suggestions based on your routine"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Turn on gesture navigation in Settings to auto-hide the Taskbar"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Long press on the divider to pin the Taskbar"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Do more with the Taskbar"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Close"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Done"</string> diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml index 5eb836b94a..04ec03f608 100644 --- a/quickstep/res/values-en-rIN/strings.xml +++ b/quickstep/res/values-en-rIN/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string> <string name="action_split" msgid="2098009717623550676">"Split"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Tap another app to use split screen"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Cancel"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Exit split screen selection"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Choose another app to use split screen"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Drag an app to the side to use two apps at once"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Slow-swipe up to show the Taskbar"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Get app suggestions based on your routine"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Turn on gesture navigation in Settings to auto-hide the Taskbar"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Long press on the divider to pin the Taskbar"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Do more with the Taskbar"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Close"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Done"</string> diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml index c0e3466695..5d705ff9c8 100644 --- a/quickstep/res/values-en-rXC/strings.xml +++ b/quickstep/res/values-en-rXC/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string> <string name="action_split" msgid="2098009717623550676">"Split"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Tap another app to use split screen"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639">""<b>"Cancel"</b>""</string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Exit split screen selection"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Choose another app to use split screen"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organization"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Drag an app to the side to use 2 apps at once"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Slow-swipe up to show the Taskbar"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Get app suggestions based on your routine"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Turn on gesture navigation in Settings to auto-hide the Taskbar"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Long press on the divider to pin the Taskbar"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Do more with the Taskbar"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Close"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Done"</string> diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml index 62aa6df8ac..a25bd7c550 100644 --- a/quickstep/res/values-es-rUS/strings.xml +++ b/quickstep/res/values-es-rUS/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Captura de pantalla"</string> <string name="action_split" msgid="2098009717623550676">"Pantalla dividida"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Presiona otra app para usar la pantalla dividida"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Cancelar"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Salir de la selección de 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">"La app o tu organización no permiten realizar esta acción"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Arrastra una app a un lado para usar 2 apps a la vez"</string> <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_pinning" msgid="6708550858580071558">"Mantén presionado el divisor para fijar la Barra de tareas"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Aprovecha mejor la Barra de tareas"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Cerrar"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Listo"</string> diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml index d261a720c3..bb2f5335d2 100644 --- a/quickstep/res/values-es/strings.xml +++ b/quickstep/res/values-es/strings.xml @@ -95,8 +95,7 @@ <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> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Cancelar"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Salir de la selección de 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> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Arrastra una aplicación hacia un lado para usar 2 a la vez"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Desliza hacia arriba lentamente para ver la barra de tareas"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Obtén sugerencias de aplicaciones basadas en tu rutina"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Activa la navegación por gestos en Ajustes para ocultar la barra de tareas automáticamente"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Mantén pulsado el divisor para fijar Barra de tareas"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Sácale más partido a la barra de tareas"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Cerrar"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Hecho"</string> diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml index 601de1d264..93fbd1c171 100644 --- a/quickstep/res/values-et/strings.xml +++ b/quickstep/res/values-et/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Ekraanipilt"</string> <string name="action_split" msgid="2098009717623550676">"Eralda"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Jagatud ekraanikuva kasutamiseks puudutage muud rakendust"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Tühista"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Jagatud ekraanikuva valikust väljumine"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Valige jagatud ekraanikuva jaoks muu rakendus"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Rakendus või teie organisatsioon on selle toimingu keelanud"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"2 rakenduse korraga kasutamiseks lohistage rakendus kõrvale"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Tegumiriba kuvamiseks pühkige aeglaselt üles"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Hankige oma rutiini põhjal rakenduste soovitusi"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Tegumiriba automaatseks peitmiseks lülitage seadetes sisse liigutustega navigeerimine"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Tegumiriba kinnitamiseks vajutage pikalt jagajat"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Tehke tegumiriba abil enamat"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Sule"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Valmis"</string> diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml index 893973b2d3..53b3390a5c 100644 --- a/quickstep/res/values-eu/strings.xml +++ b/quickstep/res/values-eu/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Atera pantaila-argazki bat"</string> <string name="action_split" msgid="2098009717623550676">"Zatitu"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Sakatu beste aplikazio bat pantaila zatitzeko"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Utzi"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Irten pantaila zatituaren hautapenetik"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Pantaila zatitzeko, aukeratu beste aplikazio bat"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikazioak edo erakundeak ez du eman ekintza hori gauzatzeko baimena"</string> @@ -110,7 +109,7 @@ <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_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_pinning" msgid="6708550858580071558">"Zereginen barra ainguratzeko, sakatu zatitzailea luze"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Egin gauza gehiago zereginen barrarekin"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Itxi"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Eginda"</string> diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml index 448bd1cbac..c8da620e86 100644 --- a/quickstep/res/values-fa/strings.xml +++ b/quickstep/res/values-fa/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"نماگرفت"</string> <string name="action_split" msgid="2098009717623550676">"دونیمه"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"زدن روی برنامهای دیگر برای استفاده از صفحه دونیمه"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"لغو کردن"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"خروج از انتخاب صفحهٔ دونیمه"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"انتخاب برنامهای دیگر برای استفاده از صفحه دونیمه"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"برنامه یا سازمان شما اجازه نمیدهد این کنش انجام شود."</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"برای سنجاق کردن «نوار وظیفه»، جداکننده را چند ثانیه فشار دهید"</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> diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml index 77e25efa71..c8c3490fe3 100644 --- a/quickstep/res/values-fi/strings.xml +++ b/quickstep/res/values-fi/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Kuvakaappaus"</string> <string name="action_split" msgid="2098009717623550676">"Jaa"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Avaa jaettu näyttö napauttamalla toista sovellusta"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Peruuta"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Poistu jaetun näytön valinnasta"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Käytä jaettua näyttöä valitsemalla toinen sovellus"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Sovellus tai organisaatio ei salli tätä toimintoa"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Vedä sovellus sivuun, ja voit käyttää kahta sovellusta"</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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Kiinnitä tehtäväpalkki painamalla jakajaa pitkään"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Vinkkejä tehtäväpalkin tehokkaampaan käyttöön"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Sulje"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Valmis"</string> diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml index 7702acd22b..8a8a9aafbf 100644 --- a/quickstep/res/values-fr-rCA/strings.xml +++ b/quickstep/res/values-fr-rCA/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Capture d\'écran"</string> <string name="action_split" msgid="2098009717623550676">"Partager"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Toucher une autre appli pour partager l\'écran"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Annuler"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Quitter la sélection d\'écran divisé"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Choisir une autre application pour utiliser l\'écran partagé"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"L\'application ou votre organisation n\'autorise pas cette action"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Pour utiliser deux applis, faites-les glisser vers le côté"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Balayez lent. vers le haut pour afficher la barre des tâches"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Obtenez des suggestions d\'applis en fonction de vos routines"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Activez la navigation par gestes dans Paramètres pour masquer auto. la barre des tâches"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Maint. doigt sur séparateur pour épingler la barre de tâches"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Faites-en plus avec la barre des tâches"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Fermer"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"OK"</string> diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml index 3876bc10a6..df6cad48dd 100644 --- a/quickstep/res/values-fr/strings.xml +++ b/quickstep/res/values-fr/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Capture d\'écran"</string> <string name="action_split" msgid="2098009717623550676">"Partager"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Appuyez sur autre appli pour l\'écran partagé"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Annuler"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Quitter la sélection de l\'écran partagé"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Sélect. autre appli pour utiliser l\'écran partagé"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Cette action n\'est pas autorisée par l\'application ou par votre organisation"</string> @@ -110,7 +109,7 @@ <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_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_pinning" msgid="6708550858580071558">"Appui de manière prolongée sur le séparateur pour épingler la barre des tâches"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Faites-en plus avec la barre des tâches"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Fermer"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"OK"</string> diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml index b5ed56ba5f..2022573a27 100644 --- a/quickstep/res/values-gl/strings.xml +++ b/quickstep/res/values-gl/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Facer captura"</string> <string name="action_split" msgid="2098009717623550676">"Dividir"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Para usar a pantalla dividida, toca outra app"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Cancelar"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Saír da selección de pantalla dividida"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Escolle outra app para usar a pantalla dividida"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"A aplicación ou a túa organización non permite realizar esta acción"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Arrastra unha aplicación cara a un lado para usar dúas á vez"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Pasa o dedo amodo cara arriba para ver a barra de tarefas"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Obtén suxestións de aplicacións en función da túa rutina"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Activa navegación con xestos en Configuración e oculta automaticamente a barra de tarefas"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Mantén premida a liña divisoria para fixar a Barra de tarefas"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Tira máis proveito da barra de tarefas"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Pechar"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Listo"</string> diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml index e3965a655c..d1adf5c09b 100644 --- a/quickstep/res/values-gu/strings.xml +++ b/quickstep/res/values-gu/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"સ્ક્રીનશૉટ"</string> <string name="action_split" msgid="2098009717623550676">"વિભાજિત કરો"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"વિભાજિત સ્ક્રીન વાપરવા, કોઈ અન્ય ઍપ પર ટૅપ કરો"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"રદ કરો"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"\'સ્ક્રીનને વિભાજિત કરો\' પસંદગીમાંથી બહાર નીકળો"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"સ્ક્રીન વિભાજનનો ઉપયોગ કરવા કોઈ અન્ય ઍપ પસંદ કરો"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ઍપ કે તમારી સંસ્થા દ્વારા આ ક્રિયા કરવાની મંજૂરી નથી"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"ટાસ્કબારને પિન કરવા માટે, વિભાજકને થોડીવાર દબાવી રાખો"</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> diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml index 431d4dd2ba..5ebe81418e 100644 --- a/quickstep/res/values-hi/strings.xml +++ b/quickstep/res/values-hi/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"स्क्रीनशॉट लें"</string> <string name="action_split" msgid="2098009717623550676">"स्प्लिट स्क्रीन मोड"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"स्प्लिट स्क्रीन के लिए दूसरे ऐप्लिकेशन पर टैप करें"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"अभी नहीं"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"स्प्लिट स्क्रीन मोड से बाहर निकलें"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"स्प्लिट स्क्रीन के लिए, दूसरा ऐप्लिकेशन चुनें"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ऐप्लिकेशन या आपका संगठन इस कार्रवाई की अनुमति नहीं देता"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"टास्कबार को पिन करने के लिए डिवाइडर को दबाकर रखें"</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> diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml index be591844d5..ad0be8e972 100644 --- a/quickstep/res/values-hr/strings.xml +++ b/quickstep/res/values-hr/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Snimka zaslona"</string> <string name="action_split" msgid="2098009717623550676">"Podijeli"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Dodirnite drugu aplikaciju za podijeljeni zaslon"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Odustani"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Zatvori odabir podijeljenog zaslona"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Odaberite drugu aplikaciju za upotrebu podijeljenog zaslona"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ili vaša organizacija ne dopuštaju ovu radnju"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Povucite aplikaciju u stranu radi istodobne upotrebe dviju aplikacija"</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_pinning" msgid="6708550858580071558">"Dugo pritisnite razdjelnik da biste prikvačili alatnu traku"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Učinite više pomoću trake sa zadacima"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Zatvori"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Gotovo"</string> diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml index 1ef8389138..b1298cebf8 100644 --- a/quickstep/res/values-hu/strings.xml +++ b/quickstep/res/values-hu/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Képernyőkép"</string> <string name="action_split" msgid="2098009717623550676">"Felosztás"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Koppintson másik appra az osztott képernyőhöz"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Mégse"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Kilépés az osztott képernyő elemeinek kiválasztásából"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Válasszon másik appot a képernyő felosztásához"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Az alkalmazás vagy az Ön szervezete nem engedélyezi ezt a műveletet"</string> @@ -110,7 +109,7 @@ <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_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_pinning" msgid="6708550858580071558">"A Feladatsáv kitűzéséhez nyomja meg hosszan az elválasztót"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Jobban kihasználhatja a Feladatsávot"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Bezárás"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Kész"</string> diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml index af97be330f..d896b510a7 100644 --- a/quickstep/res/values-hy/strings.xml +++ b/quickstep/res/values-hy/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Սքրինշոթ անել"</string> <string name="action_split" msgid="2098009717623550676">"Տրոհել"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Հպեք այլ հավելվածի՝ տրոհված էկրանից օգտվելու համար"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Չեղարկել"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Դուրս գալ տրոհված էկրանի ռեժիմից"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Ընտրեք այլ հավելված՝ կիսված էկրանից օգտվելու համար"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Այս գործողությունն արգելված է հավելվածի կամ ձեր կազմակերպության կողմից"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Հավելվածների վահանակն ամրացնելու համար երկար սեղմեք բաժանարարի վրա"</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> diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml index cc4d06a46a..e6563ae24f 100644 --- a/quickstep/res/values-in/strings.xml +++ b/quickstep/res/values-in/strings.xml @@ -95,8 +95,7 @@ <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> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Batal"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Keluar dari pemilihan 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> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Tarik aplikasi ke samping untuk menggunakan 2 aplikasi sekaligus"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Geser perlahan ke atas untuk menampilkan Taskbar"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Dapatkan saran aplikasi berdasarkan rutinitas Anda"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Aktifkan navigasi gestur di Setelan untuk menyembunyikan otomatis Taskbar"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Tekan lama pemisah untuk menyematkan Taskbar"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Lakukan lebih banyak dengan Taskbar"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Tutup"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Selesai"</string> diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml index 11d69bc9df..c297a6b46d 100644 --- a/quickstep/res/values-is/strings.xml +++ b/quickstep/res/values-is/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Skjámynd"</string> <string name="action_split" msgid="2098009717623550676">"Skipta"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Ýttu á annað forrit til að nota skjáskiptingu"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Hætta við"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Loka skjáskiptingu"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Veldu annað forrit til að nota skjáskiptingu"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Forritið eða fyrirtækið leyfir ekki þessa aðgerð"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Dragðu forrit til hliðar til að nota 2 forrit í einu"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Strjúktu hægt upp til að birta forritastikuna"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Fáðu forritatillögur sem byggjast á rútínunni þinni"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Kveiktu á bendingastjórnun í stillingunum til að fela forritastikuna sjálfkrafa"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Haltu skiptingu forritastikunnar inni til að festa hana"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Nýttu forritastikuna betur"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Loka"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Lokið"</string> diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml index 20887ac285..c028d2606d 100644 --- a/quickstep/res/values-it/strings.xml +++ b/quickstep/res/values-it/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string> <string name="action_split" msgid="2098009717623550676">"Dividi"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Tocca un\'altra app per usare lo schermo diviso"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Annulla"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Esci dalla selezione dello schermo diviso"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Scegli un\'altra app per usare lo schermo diviso"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Questa azione non è consentita dall\'app o dall\'organizzazione"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Trascina un\'app di lato per usare due app contemporaneamente"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Scorri lentamente in su per mostrare la barra delle app"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Visualizza le app suggerite in base alla tua routine"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Per nascondere automaticamente la barra delle app, attiva la navigazione tramite gesti"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Premi a lungo sul divisore per fissare la barra delle app"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Fai di più con la barra delle app"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Chiudi"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Fine"</string> diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml index 9c07653964..505096bcc4 100644 --- a/quickstep/res/values-iw/strings.xml +++ b/quickstep/res/values-iw/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"צילום מסך"</string> <string name="action_split" msgid="2098009717623550676">"פיצול"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"צריך להקיש על אפליקציה אחרת כדי להשתמש במסך מפוצל"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"ביטול"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"יציאה מתצוגת מסך מפוצל"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"כדי להשתמש במסך מפוצל צריך לבחור אפליקציה אחרת"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"האפליקציה או הארגון שלך אינם מתירים את הפעולה הזאת"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"כדי להצמיד את סרגל האפליקציות, לוחצים לחיצה ארוכה על המחיצה"</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> diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml index c753a0faba..e9f167087e 100644 --- a/quickstep/res/values-ja/strings.xml +++ b/quickstep/res/values-ja/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"スクリーンショット"</string> <string name="action_split" msgid="2098009717623550676">"分割"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"分割画面を使用するには、他のアプリをタップします"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"キャンセル"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"分割画面の選択を終了します"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"分割画面にするには、別のアプリを選択してください"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"この操作はアプリまたは組織で許可されていません"</string> @@ -110,7 +109,7 @@ <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">"[設定] でジェスチャー ナビゲーションを ON にすると、タスクバーを自動的に非表示にできます"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"タスクバーを固定するには分割線を長押ししてください"</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> diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml index 11a8900a47..5bcf85632a 100644 --- a/quickstep/res/values-ka/strings.xml +++ b/quickstep/res/values-ka/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"ეკრანის ანაბეჭდი"</string> <string name="action_split" msgid="2098009717623550676">"გაყოფა"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"შეეხეთ სხვა აპს ეკრანის გასაყოფად"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"გაუქმება"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"ეკრანის გაყოფის არჩევანიდან გასვლა"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"აირჩიეთ სხვა აპი ეკრანის გასაყოფად"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ეს მოქმედება არ არის დაშვებული აპის ან თქვენი ორგანიზაციის მიერ"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"ხანგრძლივად დააჭირეთ გამყოფს ამოცანათა ზოლის ჩასამაგრებლად"</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> diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml index d6efe1b981..da3670e0df 100644 --- a/quickstep/res/values-kk/strings.xml +++ b/quickstep/res/values-kk/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string> <string name="action_split" msgid="2098009717623550676">"Бөлу"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Экранды бөлу режимін пайдалану үшін басқа қолданбаны түртіңіз."</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Бас тарту"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Экранды бөлу режимінен шығу"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Экранды бөлу үшін басқа қолданбаны таңдаңыз."</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Бұл әрекетке қолданба не ұйым рұқсат етпейді."</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Тапсырмалар жолағын бекіту үшін бөлгішті ұзақ басып тұрыңыз"</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> diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml index 39567221ca..4c3d7323e1 100644 --- a/quickstep/res/values-km/strings.xml +++ b/quickstep/res/values-km/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"រូបថតអេក្រង់"</string> <string name="action_split" msgid="2098009717623550676">"បំបែក"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"ចុចកម្មវិធីផ្សេងទៀត ដើម្បីប្រើមុខងារបំបែកអេក្រង់"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"បោះបង់"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"ចាកចេញពីការជ្រើសរើសរបស់មុខងារបំបែកអេក្រង់"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"ជ្រើសរើសកម្មវិធីផ្សេងទៀត ដើម្បីប្រើមុខងារបំបែកអេក្រង់"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"សកម្មភាពនេះមិនត្រូវបានអនុញ្ញាតដោយកម្មវិធី ឬស្ថាប័នរបស់អ្នកទេ"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"ចុចឱ្យយូរនៅលើបន្ទាត់ខណ្ឌចែក ដើម្បីខ្ទាស់របារកិច្ចការ"</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> diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml index f8cdde0ddf..c8efb7e366 100644 --- a/quickstep/res/values-kn/strings.xml +++ b/quickstep/res/values-kn/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"ಸ್ಕ್ರೀನ್ಶಾಟ್"</string> <string name="action_split" msgid="2098009717623550676">"ವಿಭಜಿಸಿ"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಬಳಸಲು ಬೇರೆ ಆ್ಯಪ್ ಟ್ಯಾಪ್ ಮಾಡಿ"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"ರದ್ದುಮಾಡಿ"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಆಯ್ಕೆಯಿಂದ ನಿರ್ಗಮಿಸಿ"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"\"ಪರದೆ ಬೇರ್ಪಡಿಸಿ\" ಬಳಸಲು ಬೇರೆ ಆ್ಯಪ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ಆ್ಯಪ್ ಅಥವಾ ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಕ್ರಿಯೆಯನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"ಟಾಸ್ಕ್ ಬಾರ್ ಅನ್ನು ಪಿನ್ ಮಾಡಲು ಡಿವೈಡರ್ ಮೇಲೆ ದೀರ್ಘಕಾಲ ಒತ್ತಿರಿ"</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> diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml index fb036776c6..5b5b728027 100644 --- a/quickstep/res/values-ko/strings.xml +++ b/quickstep/res/values-ko/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"스크린샷"</string> <string name="action_split" msgid="2098009717623550676">"분할"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"다른 앱을 탭하여 화면 분할 사용"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"취소"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"화면 분할 선택 종료"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"화면 분할을 사용하려면 다른 앱을 선택하세요."</string> <string name="blocked_by_policy" msgid="2071401072261365546">"이 작업은 앱 또는 조직에서 허용되지 않습니다."</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"구분선을 길게 눌러 태스크 바 고정하기"</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> diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml index 80203130b0..ea98e71513 100644 --- a/quickstep/res/values-ky/strings.xml +++ b/quickstep/res/values-ky/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string> <string name="action_split" msgid="2098009717623550676">"Бөлүү"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Экранды бөлүү үчүн башка колдонмону таптап коюңуз"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Жокко чыгаруу"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Тандалган экранды бөлүүдөн чыгуу"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Экранды бөлүү үчүн башка колдонмону тандаңыз"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Бул аракетти аткарууга колдонмо же ишканаңыз тыюу салган"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Тапшырмалар панелин кадап коюу үчүн бөлгүчтү коё бербей басып туруңуз"</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> diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml index b0b4597271..635523e89a 100644 --- a/quickstep/res/values-lo/strings.xml +++ b/quickstep/res/values-lo/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"ຮູບໜ້າຈໍ"</string> <string name="action_split" msgid="2098009717623550676">"ແບ່ງ"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"ແຕະແອັບອື່ນເພື່ອໃຊ້ໜ້າຈໍແຍກ"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"ຍົກເລີກ"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"ອອກຈາກາກນເລືອກການແບ່ງໜ້າຈໍ"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"ເລືອກແອັບອື່ນເພື່ອໃຊ້ການແບ່ງໜ້າຈໍ"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ແອັບ ຫຼື ອົງການຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ໃຊ້ຄຳສັ່ງນີ້"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"ກົດຕົວຂັ້ນຄ້າງໄວ້ເພື່ອປັກໝຸດແຖບໜ້າວຽກ"</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> diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml index 34a2878f6d..c5839e3679 100644 --- a/quickstep/res/values-lt/strings.xml +++ b/quickstep/res/values-lt/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Ekrano kopija"</string> <string name="action_split" msgid="2098009717623550676">"Išskaidymo režimas"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Išskaidyto ekrano režimas palietus kitą programą"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Atšaukti"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Išeiti iš išskaidyto ekrano pasirinkimo"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Išskaidyto ekrano režimą naudokite kita programa"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Jūsų organizacijoje arba naudojant šią programą neleidžiama atlikti šio veiksmo"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Nuvilkę programą į šoną vienu metu naudokite dvi programas"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Trumpai perbraukite, kad būtų rodoma Užduočių juosta"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Gaukite programų pasiūlymų pagal savo veiklą"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Įjungę naršymą gestais „Nustatymų“ skiltyje automatiškai paslėpkite Užduočių juostą"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Ilgai paspauskite daliklį, kad prisegtumėte užduočių juostą"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Atlikite daugiau naudodami Užduočių juostą"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Uždaryti"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Atlikta"</string> diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml index 5e7b550fdc..063a6267ce 100644 --- a/quickstep/res/values-lv/strings.xml +++ b/quickstep/res/values-lv/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Veikt ekrānuzņēmumu"</string> <string name="action_split" msgid="2098009717623550676">"Sadalīt"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Lai sadalītu ekrānu, pieskarieties citai lietotnei"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Atcelt"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Izejiet no ekrāna sadalīšanas režīma atlases."</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Izvēlieties citu lietotni, lai sadalītu ekrānu"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Lietotne vai jūsu organizācija neatļauj veikt šo darbību."</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Velciet lietotni sānis, lai izmantotu 2 lietotnes vienlaikus"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Lai skatītu uzdevumu joslu, lēni velciet augšup"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Skatiet ieteiktās lietotnes, balstoties uz jūsu ieradumiem"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Lai Uzdevumu josla tiktu automātiski paslēpta, iestatījumos ieslēdziet žestu navigāciju."</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Nospiediet/turiet atdalītāju, lai piespraustu uzdevumu joslu"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Plašākas iespējas, izmantojot uzdevumu joslu"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Aizvērt"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Gatavs"</string> diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml index c4d2fb2257..61d34dd830 100644 --- a/quickstep/res/values-mk/strings.xml +++ b/quickstep/res/values-mk/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Слика од екранот"</string> <string name="action_split" msgid="2098009717623550676">"Раздели"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Допрете друга аплик. за да користите поделен екран"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Откажи"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Излези од изборот на поделен екран"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Изберете друга апликација за да користите поделен екран"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Апликацијата или вашата организација не го дозволува дејствово"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Притиснете долго на разделникот за да ја закачите „Лентата со задачи“"</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> diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml index 4316396336..f9e78271ba 100644 --- a/quickstep/res/values-ml/strings.xml +++ b/quickstep/res/values-ml/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"സ്ക്രീൻഷോട്ട്"</string> <string name="action_split" msgid="2098009717623550676">"വിഭജിക്കുക"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"സ്പ്ലിറ്റ് സ്ക്രീനിന് മറ്റൊരു ആപ്പിൽ ടാപ്പ് ചെയ്യൂ"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"റദ്ദാക്കുക"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"സ്ക്രീൻ വിഭജന തിരഞ്ഞെടുപ്പിൽ നിന്ന് പുറത്തുകടക്കുക"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"സ്ക്രീൻ വിഭജന മോഡിന് മറ്റൊരു ആപ്പ് തിരഞ്ഞെടുക്കൂ"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ഈ നടപടി എടുക്കുന്നത് ആപ്പോ നിങ്ങളുടെ സ്ഥാപനമോ അനുവദിക്കുന്നില്ല"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"ടാസ്ക്ബാർ പിൻ ചെയ്യാൻ ഡിവൈഡറിൽ ദീർഘനേരം അമർത്തുക"</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> diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml index 98b27124d6..1b8694892a 100644 --- a/quickstep/res/values-mn/strings.xml +++ b/quickstep/res/values-mn/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Дэлгэцийн агшин дарах"</string> <string name="action_split" msgid="2098009717623550676">"Хуваах"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Дэлгэцийг хуваахыг ашиглахын тулд өөр аппыг товш"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Цуцлах"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Дэлгэцийг хуваах сонголтоос гарах"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Дэлгэцийг хуваах горим ашиглах өөр апп сонгоно уу"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Энэ үйлдлийг апп эсвэл танай байгууллага зөвшөөрдөггүй"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Ажлын хэсгийг бэхлэхийн тулд тусгаарлагчийг удаан дарна уу"</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> diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml index 4bbfc6becc..722e17d781 100644 --- a/quickstep/res/values-mr/strings.xml +++ b/quickstep/res/values-mr/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"स्क्रीनशॉट"</string> <string name="action_split" msgid="2098009717623550676">"स्प्लिट"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"स्प्लिट स्क्रीन वापरण्यासाठी दुसऱ्या ॲपवर टॅप करा"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"रद्द करा"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"स्प्लिट स्क्रीन निवडीतून बाहेर पडा"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"स्प्लिट स्क्रीन वापरण्यासाठी दुसरे ॲप निवडा"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"अॅप किंवा तुमच्या संस्थेद्वारे ही क्रिया करण्याची अनुमती नाही"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"टास्कबार पिन करण्यासाठी विभाजकावर प्रेस करून ठेवा"</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> diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml index aa0f535e41..1959cd76f3 100644 --- a/quickstep/res/values-ms/strings.xml +++ b/quickstep/res/values-ms/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Tangkapan skrin"</string> <string name="action_split" msgid="2098009717623550676">"Pisah"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Ketik apl lain untuk menggunakan skrin pisah"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Batal"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Keluar daripada pilihan skrin pisah"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Pilih apl lain untuk menggunakan skrin pisah"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Tindakan ini tidak dibenarkan oleh apl atau organisasi anda"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Seret apl ke tepi untuk menggunakan 2 apl serentak"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Leret perlahan ke atas untuk menunjukkan Bar Tugas"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Dapatkan cadangan apl berdasarkan rutin anda"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Hidupkan navigasi gerak isyarat dalam Tetapan untuk autosembunyi Bar Tugas"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Tekan lama pada pembahagi untuk menyematkan Bar Tugas"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Lakukan lebih banyak perkara dengan Bar Tugas"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Tutup"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Selesai"</string> diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml index 60d82477f6..96ef54ab2e 100644 --- a/quickstep/res/values-my/strings.xml +++ b/quickstep/res/values-my/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"ဖန်သားပြင်ဓာတ်ပုံ"</string> <string name="action_split" msgid="2098009717623550676">"ခွဲထုတ်ရန်"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"မျက်နှာပြင် ခွဲ၍ပြသရန် အက်ပ်နောက်တစ်ခုကို တို့ပါ"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"မလုပ်တော့"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"မျက်နှာပြင် ခွဲ၍ပြသခြင်း ရွေးချယ်မှုမှ ထွက်ရန်"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"မျက်နှာပြင်ခွဲ၍ပြသခြင်းသုံးရန် နောက်အက်ပ်တစ်ခုရွေးပါ"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ဤလုပ်ဆောင်ချက်ကို အက်ပ် သို့မဟုတ် သင်၏အဖွဲ့အစည်းက ခွင့်မပြုပါ"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"အက်ပ် ၂ ခု တစ်ပြိုင်တည်းသုံးရန် အက်ပ်ကို ဘေးသို့ ဖိဆွဲပါ"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Taskbar ပြရန် အပေါ်သို့ ဖြည်းဖြည်းပွတ်ဆွဲပါ"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"ပုံမှန်အစီအစဉ်ပေါ် အခြေခံ၍ အက်ပ်အကြံပြုချက်များကို ရယူပါ"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Taskbar ကို အော်တိုဝှက်ရန် ဆက်တင်များတွင် လက်ဟန်ဖြင့်လမ်းညွှန်ခြင်း ဖွင့်နိုင်သည်"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Taskbar ပင်ထိုးရန် ခွဲခြားမျဉ်းကို ဖိနှိပ်ပါ"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Taskbar ဖြင့် ပိုမိုလုပ်ဆောင်နိုင်ခြင်း"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"ပိတ်ရန်"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"ပြီးပြီ"</string> diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml index 20ea562bce..f129361965 100644 --- a/quickstep/res/values-nb/strings.xml +++ b/quickstep/res/values-nb/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Skjermdump"</string> <string name="action_split" msgid="2098009717623550676">"Del opp"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Trykk på en annen app for å bruke delt skjerm"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Avbryt"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Avslutt valg av delt skjerm"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Velg en annen app for å bruke delt skjerm"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller organisasjonen din tillater ikke denne handlingen"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Dra en app til siden for å bruke 2 apper samtidig"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Sveip langsomt opp for å vise oppgavelinjen"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Få appforslag som er basert på rutinene dine"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Slå på navigasjon med bevegelser i innstillingene for å skjule oppgavelinjen automatisk"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Trykk lenge på skillelinjen for å feste oppgavelinjen"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Gjør mer med oppgavelinjen"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Lukk"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Ferdig"</string> diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml index 8b517beed7..7ff28f6278 100644 --- a/quickstep/res/values-ne/strings.xml +++ b/quickstep/res/values-ne/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"स्क्रिनसट"</string> <string name="action_split" msgid="2098009717623550676">"स्प्लिट गर्नुहोस्"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"स्प्लिटस्क्रिन प्रयोग गर्न अर्को एपमा ट्याप गर्नु…"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"रद्द गर्नुहोस्"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"स्प्लिट स्क्रिन मोडबाट बाहिरिनुहोस्"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"स्प्लिट स्क्रिन प्रयोग गर्न अर्को एप रोज्नुहोस्"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"यो एप वा तपाईंको सङ्गठनले यो कारबाही गर्ने अनुमति दिँदैन"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"टास्कबार पिन गर्न डिभाइडरमा केही बेरसम्म थिच्नुहोस्"</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> diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml index 16d8c76a01..bf10bf215c 100644 --- a/quickstep/res/values-nl/strings.xml +++ b/quickstep/res/values-nl/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string> <string name="action_split" msgid="2098009717623550676">"Splitsen"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Tik op nog een app om je scherm te splitsen"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Annuleren"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Sluit de selectie voor gesplitst scherm"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Kies andere app om gesplitst scherm te gebruiken"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Deze actie wordt niet toegestaan door de app of je organisatie"</string> @@ -110,7 +109,7 @@ <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_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_pinning" msgid="6708550858580071558">"Houd je vinger op de scheiding om de taakbalk vast te zetten"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Doe meer met de taakbalk"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Sluiten"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Klaar"</string> diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml index 367af6b26a..d9f49e6b4f 100644 --- a/quickstep/res/values-or/strings.xml +++ b/quickstep/res/values-or/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"ସ୍କ୍ରିନସଟ୍"</string> <string name="action_split" msgid="2098009717623550676">"ସ୍ପ୍ଲିଟ୍"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"ସ୍ପ୍ଲିଟସ୍କ୍ରିନ ବ୍ୟବହାର କରିବାକୁ ଅନ୍ୟ ଏକ ଆପରେ ଟାପ କର"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"ବାତିଲ କରନ୍ତୁ"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ଚୟନରୁ ବାହାରି ଯାଆନ୍ତୁ"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବାକୁ ଅନ୍ୟ ଏକ ଆପ ବାଛନ୍ତୁ"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ଆପ୍ କିମ୍ବା ଆପଣଙ୍କ ସଂସ୍ଥା ଦ୍ୱାରା ଏହି କାର୍ଯ୍ୟକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"ଟାସ୍କବାର ପିନ କରିବା ପାଇଁ ଡିଭାଇଡରକୁ ଅଧିକ ସମୟ ଦବାନ୍ତୁ"</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> diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml index c1f60d3fe9..cdf4d299f5 100644 --- a/quickstep/res/values-pa/strings.xml +++ b/quickstep/res/values-pa/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string> <string name="action_split" msgid="2098009717623550676">"ਸਪਲਿਟ"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਨੂੰ ਵਰਤਣ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ \'ਤੇ ਟੈਪ ਕਰੋ"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"ਰੱਦ ਕਰੋ"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਚੋਣ ਤੋਂ ਬਾਹਰ ਜਾਓ"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਵਰਤਣ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ ਨੂੰ ਚੁਣੋ"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਇਸ ਕਾਰਵਾਈ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"ਟਾਸਕਬਾਰ \'ਤੇ ਪਿੰਨ ਕਰਨ ਲਈ ਵਿਭਾਜਕ \'ਤੇ ਦਬਾਈ ਰੱਖੋ"</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> diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml index 30771f2dfc..7cc04ec30e 100644 --- a/quickstep/res/values-pl/strings.xml +++ b/quickstep/res/values-pl/strings.xml @@ -95,8 +95,7 @@ <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> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Anuluj"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Wyjdź z wyboru podzielonego ekranu"</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> @@ -110,7 +109,7 @@ <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_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_pinning" msgid="6708550858580071558">"Przytrzymaj separator, aby przypiąć pasek aplikacji"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Wykorzystaj potencjał paska aplikacji"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Zamknij"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Gotowe"</string> diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml index 7b548ae181..b90863d246 100644 --- a/quickstep/res/values-pt-rPT/strings.xml +++ b/quickstep/res/values-pt-rPT/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Fazer captura de ecrã"</string> <string name="action_split" msgid="2098009717623550676">"Dividir"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Toque noutra app para usar o ecrã dividido"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Cancelar"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Saia da seleção de ecrã dividido"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Escolher outra app para usar o ecrã dividido"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Esta ação não é permitida pela app ou a sua entidade."</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Arraste uma app para o lado para usar 2 apps em simultâneo"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Deslize lentamente para cima para mostrar a Barra de tarefas"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Obtenha sugestões de apps baseadas na sua rotina"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Ative a navegação por gestos em Definições para ocultar automaticamente a Barra de tarefas"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Mantenha o divisor premido para fixar a Barra de tarefas"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Faça mais com a Barra de tarefas"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Fechar"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Concluir"</string> diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml index 318da8503c..2e4e2f8ced 100644 --- a/quickstep/res/values-pt/strings.xml +++ b/quickstep/res/values-pt/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Capturar tela"</string> <string name="action_split" msgid="2098009717623550676">"Dividir"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Toque em outro app para usar a tela dividida"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Cancelar"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Sair da seleção de tela dividida"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Escolha outro app para usar na tela dividida"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Essa ação não é permitida pelo app ou pela organização"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Arraste um app para o lado e use dois apps ao mesmo tempo"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Deslize para cima devagar para mostrar a Barra de tarefas"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Receba sugestões de apps com base na sua rotina"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Ative a navegação por gestos nas configs. para ocultar a Barra de tarefas automaticamente"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Mantenha o separador pressionado para fixar a Barra de tarefas"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Aproveite ainda mais a Barra de tarefas"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Fechar"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Concluído"</string> diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml index 2703997a52..2f40dc4279 100644 --- a/quickstep/res/values-ro/strings.xml +++ b/quickstep/res/values-ro/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Captură de ecran"</string> <string name="action_split" msgid="2098009717623550676">"Împărțit"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Atinge altă aplicație pentru ecranul împărțit"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Anulează"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Ieși din selecția cu ecran împărțit"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Alege altă aplicație pentru ecranul împărțit"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Această acțiune nu este permisă de aplicație sau de organizația ta"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Trage în lateral o aplicație ca să folosești 2 aplicații deodată"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Glisează lent în sus pentru a afișa bara de activități"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Primește sugestii de aplicații în funcție de rutina ta"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Activează navigarea prin gesturi în Setări ca să ascunzi automat bara de activități"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Apasă lung pe separator pentru a fixa Bara de activități"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Fă mai multe din Bara de activități"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Închide"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Gata"</string> diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml index 533b1a387f..0b7a193098 100644 --- a/quickstep/res/values-ru/strings.xml +++ b/quickstep/res/values-ru/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string> <string name="action_split" msgid="2098009717623550676">"Разделить"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Для разделения экрана выберите другое приложение."</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Отмена"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Выйдите из режима разделения экрана."</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Выберите другое приложение для разделения экрана."</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Это действие заблокировано приложением или организацией."</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Закрепите панель задач долгим нажатием на разделитель"</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> diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml index c8f350a530..0801a53178 100644 --- a/quickstep/res/values-si/strings.xml +++ b/quickstep/res/values-si/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"තිර රුව"</string> <string name="action_split" msgid="2098009717623550676">"බෙදන්න"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"බෙදුම් තිරය භාවිතා කිරීමට තවත් යෙදුමක් තට්ටු කරන්න"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"අවලංගු කරන්න"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"බෙදීම් තිර තේරීමෙන් පිටවන්න"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"බෙදීම් තිරය භාවිතා කිරීමට වෙනත් යෙදුමක් තෝරා ගන්න"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"මෙම ක්රියාව යෙදුම හෝ ඔබේ සංවිධානය මගින් ඉඩ නොදේ"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"කාර්ය තීරුව ඇමිණීමට බෙදනය මත දිගු වේලාවක් ඔබන්න"</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> diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml index 5b99d42e17..7e1618433c 100644 --- a/quickstep/res/values-sk/strings.xml +++ b/quickstep/res/values-sk/strings.xml @@ -95,8 +95,7 @@ <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> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Zrušiť"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Ukončite výber rozdelenej obrazovky"</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> @@ -110,7 +109,7 @@ <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_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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Dlhým stlačením rozdeľovača pripnete panel aplikácií"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Panel aplikácií vám ponúka ďalšie možnosti"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Zavrieť"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Hotovo"</string> diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml index 5ab0c02052..9df20c223e 100644 --- a/quickstep/res/values-sl/strings.xml +++ b/quickstep/res/values-sl/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Posnetek zaslona"</string> <string name="action_split" msgid="2098009717623550676">"Razdeli"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Za razdeljeni zaslon se dotaknite še 1 aplikacije"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Prekliči"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Zapri izbiro razdeljenega zaslona"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Izberite drugo aplikacijo za uporabo razdeljenega zaslona."</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ali vaša organizacija ne dovoljuje tega dejanja"</string> @@ -110,7 +109,7 @@ <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_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_pinning" msgid="6708550858580071558">"Pridržite razdelilno črto, da pripnete opravilno vrstico"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Naredite več z opravilno vrstico"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Zapri"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Končano"</string> diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml index fa83899657..566a42dffd 100644 --- a/quickstep/res/values-sq/strings.xml +++ b/quickstep/res/values-sq/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Pamja e ekranit"</string> <string name="action_split" msgid="2098009717623550676">"Ndaj"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Trokit një apl. tjetër; përdor ekranin e ndarë"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Anulo"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Dil nga zgjedhja e ekranit të ndarë"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Zgjidh një aplikacion tjetër për të përdorur ekranin e ndarë"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Ky veprim nuk lejohet nga aplikacioni ose organizata jote"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Zvarrit një aplikacion në anë për të përdorur 2 aplikacione njëherësh"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Rrëshqit lart ngadalë për të shfaqur \"Shiritin e detyrave\""</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Merr sugjerime për aplikacion bazuar në rutinën tënde"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Aktivizo navigimin me gjeste te \"Cilësimet\" për të fshehur \"Shiritin e detyrave\""</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Kryej një shtypje të gjatë te ndarësi për të gozhduar \"Shiritin e detyrave\""</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Bëj më shumë me \"Shiritin e detyrave\""</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Mbyll"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"U krye"</string> diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml index e07480269d..78ce2ab4a9 100644 --- a/quickstep/res/values-sr/strings.xml +++ b/quickstep/res/values-sr/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Снимак екрана"</string> <string name="action_split" msgid="2098009717623550676">"Подели"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Додирните другу апликацију за подељени екран"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Откажи"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Излазак из бирања подељеног екрана"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Одаберите другу апликацију за подељени екран"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Апликација или организација не дозвољавају ову радњу"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Дуго притискајте разделник да бисте закачили траку задатака"</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> diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml index c13b4379b2..a1e2048824 100644 --- a/quickstep/res/values-sv/strings.xml +++ b/quickstep/res/values-sv/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Skärmbild"</string> <string name="action_split" msgid="2098009717623550676">"Delat"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Tryck på en annan app för att använda delad skärm"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Avbryt"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Avsluta val av delad skärm"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Välj en annan app för att använda delad skärm"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller organisationen tillåter inte den här åtgärden"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Dra en app till sidan om du vill använda två appar samtidigt"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Svep långsamt uppåt för att visa aktivitetsfältet"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Få appförslag utifrån dina rutiner"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Slå på navigering med rörelser i Inställningar för att dölja aktivitetsfältet automatiskt"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Tryck länge på avskiljaren om du vill fästa aktivitetsfältet"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Gör mer med aktivitetsfältet"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Stäng"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Klar"</string> diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml index 48ba038ef5..089672cb3d 100644 --- a/quickstep/res/values-sw/strings.xml +++ b/quickstep/res/values-sw/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Picha ya skrini"</string> <string name="action_split" msgid="2098009717623550676">"Iliyogawanywa"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Gusa programu nyingine ili utumie kipengele cha kugawa skrini"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Ghairi"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Ondoka kwenye hali ya skrini iliyogawanywa"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Chagua programu nyingine ili utumie hali ya kugawa skrini"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Kitendo hiki hakiruhusiwi na programu au shirika lako"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Buruta programu pembeni ili utumie programu 2 kwa wakati mmoja"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Telezesha kidole juu taratibu ili ufungue Upauzana"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Pata mapendekezo ya programu kulingana na ratiba yako"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Washa usogezaji kwa kutumia ishara kwenye Mipangilio ili ufiche Upauzana kiotomatiki"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Bonyeza kwa muda mrefu kigawaji ili ubandike Upauzana"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Kamilisha mengi kwa kutumia Upauzana huu"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Funga"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Imemaliza"</string> diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml index fe021fb28d..49ddb8cc2d 100644 --- a/quickstep/res/values-ta/strings.xml +++ b/quickstep/res/values-ta/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"ஸ்கிரீன்ஷாட்"</string> <string name="action_split" msgid="2098009717623550676">"பிரி"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"திரைப் பிரிப்பைப் பயன்படுத்த வேறு ஆப்ஸைத் தட்டவும்"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"ரத்துசெய்"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"திரைப் பிரிப்பு தேர்வில் இருந்து வெளியேறும்"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"திரைப் பிரிப்பை பயன்படுத்த வேறு ஆப்ஸை தேர்வுசெய்க"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ஆப்ஸோ உங்கள் நிறுவனமோ இந்த செயலை அனுமதிப்பதில்லை"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"பிரிப்பானை நீண்ட நேரம் அழுத்தி, செயல் பட்டியைப் பின் செய்க"</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> diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml index 38b9636a30..d13764eaff 100644 --- a/quickstep/res/values-te/strings.xml +++ b/quickstep/res/values-te/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"స్క్రీన్షాట్"</string> <string name="action_split" msgid="2098009717623550676">"స్ప్లిట్ చేయండి"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"స్ప్లిట్ స్క్రీన్ కోసం మరొక యాప్ను ట్యాప్ చేయండి"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"రద్దు చేయండి"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"స్ప్లిట్ స్క్రీన్ ఎంపిక నుండి ఎగ్జిట్ అవ్వండి"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"స్ప్లిట్ స్క్రీన్ ఉపయోగానికి మరొక యాప్ ఎంచుకోండి"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ఈ చర్యను యాప్ గానీ, మీ సంస్థ గానీ అనుమతించవు"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"టాస్క్బార్ను పిన్ చేయడానికి డివైడర్పై ఎక్కువసేపు నొక్కి, ఉంచడం"</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> diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml index c51c794f8b..7447b6d432 100644 --- a/quickstep/res/values-th/strings.xml +++ b/quickstep/res/values-th/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"ภาพหน้าจอ"</string> <string name="action_split" msgid="2098009717623550676">"แยก"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"แตะแอปอื่นเพื่อใช้การแยกหน้าจอ"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"ยกเลิก"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"ออกจากการเลือกโหมดแยกหน้าจอ"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"เลือกแอปอื่นเพื่อใช้การแยกหน้าจอ"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"แอปหรือองค์กรของคุณไม่อนุญาตการดำเนินการนี้"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"กดตัวแบ่งค้างไว้เพื่อปักหมุดแถบงาน"</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> diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml index 4bdb331910..cd432cc73a 100644 --- a/quickstep/res/values-tl/strings.xml +++ b/quickstep/res/values-tl/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string> <string name="action_split" msgid="2098009717623550676">"Split"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Mag-tap ng ibang app para gamitin ang split screen"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Kanselahin"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Lumabas sa pagpili ng split screen"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Pumili ng ibang app para gamitin ang split screen"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Hindi pinapayagan ng app o ng iyong organisasyon ang pagkilos na ito"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Mag-drag ng app sa gilid para makagamit ng 2 app nang sabay"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Mag-swipe nang mabagal pataas para ipakita ang Taskbar"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Makakuha ng mga iminumungkahing app batay sa iyong routine"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"I-on ang navigation gamit ang galaw sa Mga Setting para i-auto hide ang Taskbar"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Pumindot nang matagal sa divider para i-pin ang Taskbar"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Mas maraming magawa gamit ang Taskbar"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Isara"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Tapos na"</string> diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml index 04043ee3e9..ed22286a9c 100644 --- a/quickstep/res/values-tr/strings.xml +++ b/quickstep/res/values-tr/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Ekran görüntüsü"</string> <string name="action_split" msgid="2098009717623550676">"Böl"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Bölünmüş ekran için başka bir uygulamaya dokunun"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"İptal"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Bölünmüş ekran seçiminden çıkın"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Bölünmüş ekran kullanmak için başka bir uygulama seçin"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Uygulamanız veya kuruluşunuz bu işleme izin vermiyor"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Aynı anda iki uygulama kullanmak için birini yana sürükleyin"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Görev çubuğunu göstermek için yukarı doğru yavaşça kaydırın"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Rutininize göre uygulama önerileri alın"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Görev çubuğunu otomatik olarak gizlemek için Ayarlar\'dan hareketle gezinmeyi etkinleştirin"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Görev çubuğunu sabitlemek için ayırıcıya uzun basın"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Görev çubuğuyla daha fazla şey yapın"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Kapat"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Bitti"</string> diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml index ea0df1a313..42fc6ddfee 100644 --- a/quickstep/res/values-uk/strings.xml +++ b/quickstep/res/values-uk/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Знімок екрана"</string> <string name="action_split" msgid="2098009717623550676">"Розділити"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Щоб розділити екран, виберіть ще один додаток"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Скасувати"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Вийти з режиму розділення екрана"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Щоб розділити екран, виберіть ще один додаток"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Ця дія заборонена додатком або адміністратором організації"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Утримуйте розділювач, щоб закріпити панель завдань"</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> diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml index 5bff0f6165..0e5b60255b 100644 --- a/quickstep/res/values-ur/strings.xml +++ b/quickstep/res/values-ur/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"اسکرین شاٹ"</string> <string name="action_split" msgid="2098009717623550676">"اسپلٹ"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"اسپلٹ اسکرین کا استعمال کرنے کیلئے دوسری ایپ پر تھپتھپائیں"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"منسوخ کریں"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"اسپلٹ اسکرین کے انتخاب سے باہر نکلیں"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"اسپلٹ اسکرین کے استعمال کیلئے دوسری ایپ منتخب کریں"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"ایپ یا آپ کی تنظیم کی جانب سے اس کارروائی کی اجازت نہیں ہے"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"ٹاسک بار کو پن کرنے کے لیے ڈیوائیڈر پر لانگ پریس کریں"</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> diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml index 4029408500..7cc157b159 100644 --- a/quickstep/res/values-uz/strings.xml +++ b/quickstep/res/values-uz/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Skrinshot"</string> <string name="action_split" msgid="2098009717623550676">"Ajratish"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Ekranni ikkiga ajratish uchun boshqa ilovani bosing"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Bekor qilish"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Ekranni ikkiga ajratish tanlovidan chiqish"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Ekranni ikkiga ajratish uchun boshqa ilovani tanlang"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Bu amal ilova yoki tashkilotingiz tomonidan taqiqlangan"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Bitta ilovani yon tomonga sudrab, bir vaqtda 2 ta ilovadan foydalaning."</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Vazifalar panelini ochish uchun tepaga asta suring"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Harakatlaringiz asosida tavsiyalar oling."</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Vazifalar paneli avtomatik yopilishi uchun ishorali navigatsiyani yoqing"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Vazifa panelini mahkamlash uchun ajratgichni bosib turing"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Vazifalar panelidan maksimal darajada foydalaning"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Yopish"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Tayyor"</string> diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml index 3232527cfa..bc9b348978 100644 --- a/quickstep/res/values-vi/strings.xml +++ b/quickstep/res/values-vi/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Chụp ảnh màn hình"</string> <string name="action_split" msgid="2098009717623550676">"Chia đôi màn hình"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Nhấn vào ứng dụng khác để chia đôi màn hình"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Huỷ"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Thoát khỏi lựa chọn chia đôi màn hình"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Chọn một ứng dụng khác để dùng chế độ chia đôi màn hình"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Ứng dụng hoặc tổ chức của bạn không cho phép thực hiện hành động này"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Kéo một ứng dụng sang bên để dùng 2 ứng dụng cùng lúc"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Từ từ vuốt lên để Thanh tác vụ xuất hiện"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Nhận ứng dụng đề xuất dựa trên thói quen của bạn"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Bật tính năng thao tác bằng cử chỉ trong phần Cài đặt để tự động ẩn Thanh tác vụ"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Nhấn và giữ trên đường phân chia để ghim Taskbar"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Làm nhiều việc hơn qua Thanh tác vụ"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Đóng"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Xong"</string> diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml index d48d9b5c11..2c740b7e7f 100644 --- a/quickstep/res/values-zh-rCN/strings.xml +++ b/quickstep/res/values-zh-rCN/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"屏幕截图"</string> <string name="action_split" msgid="2098009717623550676">"拆分"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"点按另一个应用即可使用分屏"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"取消"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"退出分屏选择模式"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"另外选择一个应用才可使用分屏模式"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"该应用或您所在的单位不允许执行此操作"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"长按分隔线即可固定任务栏"</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> diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml index 5a4ec9e5d1..af7c663d80 100644 --- a/quickstep/res/values-zh-rHK/strings.xml +++ b/quickstep/res/values-zh-rHK/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"螢幕截圖"</string> <string name="action_split" msgid="2098009717623550676">"分割"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"輕按其他應用程式以使用分割螢幕"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"取消"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"退出分割螢幕選取頁面"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"選擇其他應用程式才能使用分割螢幕"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"應用程式或你的機構不允許此操作"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"長按分隔線即可固定工作列"</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> diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml index 8ae440353d..e4ca3d913c 100644 --- a/quickstep/res/values-zh-rTW/strings.xml +++ b/quickstep/res/values-zh-rTW/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"螢幕截圖"</string> <string name="action_split" msgid="2098009717623550676">"分割"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"輕觸另一個應用程式即可使用分割畫面"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"取消"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"退出分割畫面選擇器"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"必須選擇另一個應用程式才能使用分割畫面"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"這個應用程式或貴機構不允許執行這個動作"</string> @@ -110,7 +109,7 @@ <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> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"長按分隔線即可固定工作列"</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> diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml index 4763101143..0526940f26 100644 --- a/quickstep/res/values-zu/strings.xml +++ b/quickstep/res/values-zu/strings.xml @@ -95,8 +95,7 @@ <string name="action_screenshot" msgid="8171125848358142917">"Isithombe-skrini"</string> <string name="action_split" msgid="2098009717623550676">"Hlukanisa"</string> <string name="toast_split_select_app" msgid="8464310533320556058">"Thepha enye i-app ukuze usebenzise isikrini sokuhlukanisa"</string> - <!-- no translation found for toast_split_select_app_cancel (1532690483356445639) --> - <skip /> + <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Khansela"</b></string> <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Phuma ekukhetheni ukuhlukaniswa kwesikrini"</string> <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Khetha enye i-app ukuze usebenzise ukuhlukanisa isikrini"</string> <string name="blocked_by_policy" msgid="2071401072261365546">"Lesi senzo asivunyelwanga uhlelo lokusebenza noma inhlangano yakho"</string> @@ -110,7 +109,7 @@ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Hudula i-app ukusebenzisa ama-app ama-2 ngesikhathi esisodwa"</string> <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Swayiphela phezulu kancane ukuze ubonise i-Taskbar"</string> <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Thola iziphakamiso ze-app ngokusekelwe kumjikelezo wakho"</string> - <string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Vula ukufuna kokuthinta Kumasethingi ukuze ufihle ngokuzenzakalela ibha yomsebenzi"</string> + <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Cindezela isikhathi eside kusihlukanisi ukuze uphine i-Taskbar"</string> <string name="taskbar_edu_features" msgid="3320337287472848162">"Yenza okwengeziwe nge-Taskbar"</string> <string name="taskbar_edu_close" msgid="887022990168191073">"Vala"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Kwenziwe"</string> diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 2a1f39f490..232c4416a6 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -322,6 +322,7 @@ <!-- Taskbar --> <dimen name="taskbar_size">@*android:dimen/taskbar_frame_height</dimen> + <dimen name="taskbar_phone_size">@*android:dimen/navigation_bar_frame_height</dimen> <dimen name="taskbar_ime_size">48dp</dimen> <dimen name="taskbar_icon_min_touch_size">48dp</dimen> <!-- Note that this applies to both sides of all icons, so visible space is double this. --> diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml index df32626e5c..29779a711e 100644 --- a/quickstep/res/values/override.xml +++ b/quickstep/res/values/override.xml @@ -35,4 +35,6 @@ <string name="assist_state_manager_class" translatable="false"></string> + <string name="launcher_restore_event_logger_class" translatable="false">com.android.quickstep.LauncherRestoreEventLoggerImpl</string> + </resources> diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml index ae62c2621f..bdc86b217d 100644 --- a/quickstep/res/values/styles.xml +++ b/quickstep/res/values/styles.xml @@ -311,4 +311,9 @@ <item name="android:letterSpacing">0.025</item> <item name="android:lineHeight">20sp</item> </style> + + <style name="WidgetPickerActivityTheme" parent="@android:style/Theme.Translucent.NoTitleBar"> + <item name="widgetsTheme">@style/WidgetContainerTheme</item> + <item name="android:windowBackground">@android:color/transparent</item> + </style> </resources> diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index d6ab54e039..4898761736 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -55,6 +55,7 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAU import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION; import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY; import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; +import static com.android.launcher3.testing.shared.TestProtocol.WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE; import static com.android.launcher3.util.DisplayController.isTransientTaskbar; import static com.android.launcher3.util.Executors.ORDERED_BG_EXECUTOR; import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE; @@ -120,6 +121,7 @@ import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorListeners; +import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.icons.FastBitmapDrawable; import com.android.launcher3.model.data.ItemInfo; @@ -1651,6 +1653,16 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener if (launcherIsForceInvisibleOrOpening) { addCujInstrumentation(anim, playFallBackAnimation ? CUJ_APP_CLOSE_TO_HOME_FALLBACK : CUJ_APP_CLOSE_TO_HOME); + + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + AccessibilityManagerCompat.sendTestProtocolEventToTest( + mLauncher, WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE); + } + }); + // Only register the content animation for cancellation when state changes mLauncher.getStateManager().setCurrentAnimation(anim); diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java new file mode 100644 index 0000000000..43716abd97 --- /dev/null +++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java @@ -0,0 +1,93 @@ +/* + * 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; + +import static android.view.WindowInsets.Type.navigationBars; +import static android.view.WindowInsets.Type.statusBars; + +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + +import android.os.Bundle; +import android.view.WindowInsetsController; +import android.view.WindowManager; + +import androidx.annotation.NonNull; + +import com.android.launcher3.dragndrop.SimpleDragLayer; +import com.android.launcher3.model.WidgetsModel; +import com.android.launcher3.popup.PopupDataProvider; +import com.android.launcher3.widget.BaseWidgetSheet; +import com.android.launcher3.widget.model.WidgetsListBaseEntry; +import com.android.launcher3.widget.picker.WidgetsFullSheet; + +import java.util.ArrayList; + +/** An Activity that can host Launcher's widget picker. */ +public class WidgetPickerActivity extends BaseActivity { + private SimpleDragLayer<WidgetPickerActivity> mDragLayer; + private WidgetsModel mModel; + private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(i -> {}); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER); + + LauncherAppState app = LauncherAppState.getInstance(this); + InvariantDeviceProfile idp = app.getInvariantDeviceProfile(); + + mDeviceProfile = idp.getDeviceProfile(this); + mModel = new WidgetsModel(); + + setContentView(R.layout.widget_picker_activity); + mDragLayer = findViewById(R.id.drag_layer); + mDragLayer.recreateControllers(); + + WindowInsetsController wc = mDragLayer.getWindowInsetsController(); + wc.hide(navigationBars() + statusBars()); + + BaseWidgetSheet widgetSheet = WidgetsFullSheet.show(this, true); + widgetSheet.disableNavBarScrim(true); + widgetSheet.addOnCloseListener(this::finish); + + refreshAndBindWidgets(); + } + + @NonNull + @Override + public PopupDataProvider getPopupDataProvider() { + return mPopupDataProvider; + } + + @Override + public SimpleDragLayer<WidgetPickerActivity> getDragLayer() { + return mDragLayer; + } + + private void refreshAndBindWidgets() { + MODEL_EXECUTOR.execute(() -> { + LauncherAppState app = LauncherAppState.getInstance(this); + mModel.update(app, null); + final ArrayList<WidgetsListBaseEntry> widgets = + mModel.getWidgetsListForPicker(app.getContext()); + MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setAllWidgets(widgets)); + }); + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java index 29c5204e7c..0a9dfff568 100644 --- a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java @@ -18,12 +18,15 @@ package com.android.launcher3.taskbar; import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_NOTIFICATIONS; import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_QUICK_SETTINGS; +import android.content.Context; import android.content.pm.ActivityInfo.Config; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import androidx.annotation.Nullable; + import com.android.launcher3.R; /** @@ -40,8 +43,8 @@ public class DesktopNavbarButtonsViewController extends NavbarButtonsViewControl private TaskbarControllers mControllers; public DesktopNavbarButtonsViewController(TaskbarActivityContext context, - FrameLayout navButtonsView) { - super(context, navButtonsView); + @Nullable Context navigationBarPanelContext, FrameLayout navButtonsView) { + super(context, navigationBarPanelContext, navButtonsView); mContext = context; mNavButtonsView = navButtonsView; mNavButtonContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons); diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java index cbb991da82..b29ce6be14 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java @@ -133,11 +133,20 @@ public class KeyboardQuickSwitchViewController { GroupTask task = mControllerCallbacks.getTaskAt(index); if (task == null) { return Math.max(0, index); - } else if (mOnDesktop) { + } + Task task2 = task.task2; + int runningTaskId = ActivityManagerWrapper.getInstance().getRunningTask().taskId; + if (runningTaskId == task.task1.key.id + || (task2 != null && runningTaskId == task2.key.id)) { + // Ignore attempts to run the selected task if it is already running. + return -1; + } + + if (mOnDesktop) { UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(mKeyboardQuickSwitchView.getContext()) .showDesktopApp(task.task1.key.id)); - } else if (task.task2 == null) { + } else if (task2 == null) { UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance().startActivityFromRecents( task.task1.key, @@ -145,8 +154,7 @@ public class KeyboardQuickSwitchViewController { taskView == null ? mKeyboardQuickSwitchView : taskView, null) .options)); } else { - mControllers.uiController.launchSplitTasks( - taskView == null ? mKeyboardQuickSwitchView : taskView, task); + mControllers.uiController.launchSplitTasks(task); } return -1; } diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index bbe73fff6d..159a6eff07 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -26,7 +26,6 @@ import android.animation.AnimatorSet; import android.os.RemoteException; import android.util.Log; import android.view.TaskTransitionSpec; -import android.view.View; import android.view.WindowManagerGlobal; import androidx.annotation.NonNull; @@ -205,6 +204,11 @@ public class LauncherTaskbarUIController extends TaskbarUIController { } @Override + public void onStateTransitionCompletedAfterSwipeToHome(LauncherState state) { + mTaskbarLauncherStateController.onStateTransitionCompletedAfterSwipeToHome(state); + } + + @Override public void refreshResumedState() { onLauncherVisibilityChanged(mLauncher.hasBeenResumed()); } @@ -386,8 +390,8 @@ public class LauncherTaskbarUIController extends TaskbarUIController { } @Override - public void launchSplitTasks(@NonNull View taskView, @NonNull GroupTask groupTask) { - mLauncher.launchSplitTasks(taskView, groupTask); + public void launchSplitTasks(@NonNull GroupTask groupTask) { + mLauncher.launchSplitTasks(groupTask); } @Override diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java index 3514447ad1..bed4c376ae 100644 --- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java @@ -53,6 +53,7 @@ import android.animation.ObjectAnimator; import android.annotation.DrawableRes; import android.annotation.IdRes; import android.annotation.LayoutRes; +import android.content.Context; import android.content.pm.ActivityInfo.Config; import android.content.res.ColorStateList; import android.content.res.Resources; @@ -80,6 +81,8 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; +import androidx.annotation.Nullable; + import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.R; @@ -146,6 +149,7 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT private int mState; private final TaskbarActivityContext mContext; + private final @Nullable Context mNavigationBarPanelContext; private final WindowManagerProxy mWindowManagerProxy; private final FrameLayout mNavButtonsView; private final LinearLayout mNavButtonContainer; @@ -203,8 +207,10 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT private final RecentsHitboxExtender mHitboxExtender = new RecentsHitboxExtender(); private ImageView mRecentsButton; - public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) { + public NavbarButtonsViewController(TaskbarActivityContext context, + @Nullable Context navigationBarPanelContext, FrameLayout navButtonsView) { mContext = context; + mNavigationBarPanelContext = navigationBarPanelContext; mWindowManagerProxy = WindowManagerProxy.INSTANCE.get(mContext); mNavButtonsView = navButtonsView; mNavButtonContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons); @@ -312,7 +318,8 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT rotationButton.hide(); mControllers.rotationButtonController.setRotationButton(rotationButton, null); } else { - mFloatingRotationButton = new FloatingRotationButton(mContext, + mFloatingRotationButton = new FloatingRotationButton( + ENABLE_TASKBAR_NAVBAR_UNIFICATION ? mNavigationBarPanelContext : mContext, R.string.accessibility_rotate_button, R.layout.rotate_suggestion, R.id.rotate_suggestion, diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java index c4255bf70f..da1f766420 100644 --- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java @@ -108,7 +108,7 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT DeviceProfile deviceProfile = mActivity.getDeviceProfile(); Resources resources = mActivity.getResources(); if (isPhoneGestureNavMode(mActivity.getDeviceProfile())) { - mTaskbarSize = resources.getDimensionPixelSize(R.dimen.taskbar_size); + mTaskbarSize = resources.getDimensionPixelSize(R.dimen.taskbar_phone_size); mStashedHandleWidth = resources.getDimensionPixelSize(R.dimen.taskbar_stashed_small_screen); } else { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 988ef807fe..4290948ad2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -74,9 +74,11 @@ import androidx.annotation.VisibleForTesting; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherPrefs; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.R; import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.apppairs.AppPairIcon; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dot.DotInfo; import com.android.launcher3.folder.Folder; @@ -106,6 +108,7 @@ import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy; import com.android.launcher3.util.ActivityOptionsWrapper; +import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.Executors; import com.android.launcher3.util.NavigationMode; @@ -127,6 +130,7 @@ import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider; import java.io.PrintWriter; import java.util.Collections; +import java.util.List; import java.util.Optional; import java.util.function.Consumer; @@ -143,6 +147,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { private static final String WINDOW_TITLE = "Taskbar"; + private final @Nullable Context mNavigationBarPanelContext; + private final TaskbarDragLayer mDragLayer; private final TaskbarControllers mControllers; @@ -178,11 +184,15 @@ public class TaskbarActivityContext extends BaseTaskbarContext { private DeviceProfile mPersistentTaskbarDeviceProfile; - public TaskbarActivityContext(Context windowContext, DeviceProfile launcherDp, + private final LauncherPrefs mLauncherPrefs; + + public TaskbarActivityContext(Context windowContext, + @Nullable Context navigationBarPanelContext, DeviceProfile launcherDp, TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider unfoldTransitionProgressProvider) { super(windowContext); + mNavigationBarPanelContext = navigationBarPanelContext; applyDeviceProfile(launcherDp); final Resources resources = getResources(); @@ -203,11 +213,16 @@ public class TaskbarActivityContext extends BaseTaskbarContext { Display display = windowContext.getDisplay(); Context c = getApplicationContext(); mWindowManager = c.getSystemService(WindowManager.class); - mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT); - mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT); - // Inflate views. boolean phoneMode = TaskbarManager.isPhoneMode(mDeviceProfile); + mLeftCorner = phoneMode + ? null + : display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT); + mRightCorner = phoneMode + ? null + : display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT); + + // Inflate views. int taskbarLayout = DisplayController.isTransientTaskbar(this) && !phoneMode ? R.layout.transient_taskbar : R.layout.taskbar; @@ -251,8 +266,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext { new TaskbarDragController(this), buttonController, isDesktopMode - ? new DesktopNavbarButtonsViewController(this, navButtonsView) - : new NavbarButtonsViewController(this, navButtonsView), + ? new DesktopNavbarButtonsViewController(this, mNavigationBarPanelContext, + navButtonsView) + : new NavbarButtonsViewController(this, mNavigationBarPanelContext, + navButtonsView), rotationButtonController, new TaskbarDragLayerController(this, mDragLayer), new TaskbarViewController(this, taskbarView), @@ -280,6 +297,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { new KeyboardQuickSwitchController(), new TaskbarPinningController(this), bubbleControllersOptional); + + mLauncherPrefs = LauncherPrefs.get(this); } /** Updates {@link DeviceProfile} instances for any Taskbar windows. */ @@ -397,6 +416,11 @@ public class TaskbarActivityContext extends BaseTaskbarContext { getDeviceProfile().toSmallString()); } + @NonNull + public LauncherPrefs getLauncherPrefs() { + return mLauncherPrefs; + } + /** * Returns the View bounds of transient taskbar. */ @@ -861,7 +885,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { if (ENABLE_TASKBAR_NAVBAR_UNIFICATION && mDeviceProfile.isPhone) { return isThreeButtonNav() ? - resources.getDimensionPixelSize(R.dimen.taskbar_size) : + resources.getDimensionPixelSize(R.dimen.taskbar_phone_size) : resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size); } @@ -970,6 +994,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } protected void onTaskbarIconClicked(View view) { + TaskbarUIController taskbarUIController = mControllers.uiController; + RecentsView recents = taskbarUIController.getRecentsView(); boolean shouldCloseAllOpenViews = true; Object tag = view.getTag(); if (tag instanceof Task) { @@ -977,41 +1003,26 @@ public class TaskbarActivityContext extends BaseTaskbarContext { ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key, ActivityOptions.makeBasic()); mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); - } else if (tag instanceof FolderInfo) { + } else if (tag instanceof FolderInfo fi && fi.itemType == Favorites.ITEM_TYPE_FOLDER) { + // Tapping an expandable folder icon on Taskbar shouldCloseAllOpenViews = false; - FolderIcon folderIcon = (FolderIcon) view; - Folder folder = folderIcon.getFolder(); - - folder.setOnFolderStateChangedListener(newState -> { - if (newState == Folder.STATE_OPEN) { - setTaskbarWindowFocusableForIme(true); - } else if (newState == Folder.STATE_CLOSED) { - // Defer by a frame to ensure we're no longer fullscreen and thus won't jump. - getDragLayer().post(() -> setTaskbarWindowFocusableForIme(false)); - folder.setOnFolderStateChangedListener(null); - } - }); - - setTaskbarWindowFullscreen(true); - - getDragLayer().post(() -> { - folder.animateOpen(); - getStatsLogManager().logger().withItemInfo(folder.mInfo).log(LAUNCHER_FOLDER_OPEN); - - folder.iterateOverItems((itemInfo, itemView) -> { - mControllers.taskbarViewController - .setClickAndLongClickListenersForIcon(itemView); - // To play haptic when dragging, like other Taskbar items do. - itemView.setHapticFeedbackEnabled(true); - return false; - }); - }); + expandFolder((FolderIcon) view); + } else if (tag instanceof FolderInfo fi && fi.itemType == Favorites.ITEM_TYPE_APP_PAIR) { + // Tapping an app pair icon on Taskbar + if (recents != null && recents.isSplitSelectionActive()) { + // TODO (b/274835596): Implement "can't split with this" bounce animation + Toast.makeText(this, "Unable to split with an app pair. Select another app.", + Toast.LENGTH_SHORT).show(); + } else { + // Else launch the selected app pair + launchFromTaskbarPreservingSplitIfVisible(recents, view, fi.contents); + mControllers.uiController.onTaskbarIconLaunched(fi); + mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); + } } else if (tag instanceof WorkspaceItemInfo) { // Tapping a launchable icon on Taskbar WorkspaceItemInfo info = (WorkspaceItemInfo) tag; if (!info.isDisabled() || !ItemClickHandler.handleDisabledItemClicked(info, this)) { - TaskbarUIController taskbarUIController = mControllers.uiController; - RecentsView recents = taskbarUIController.getRecentsView(); if (recents != null && recents.isSplitSelectionActive()) { // If we are selecting a second app for split, launch the split tasks taskbarUIController.triggerSecondAppForSplit(info, info.intent, view); @@ -1039,7 +1050,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { getSystemService(LauncherApps.class) .startShortcut(packageName, id, null, null, info.user); } else { - launchFromTaskbarPreservingSplitIfVisible(recents, info); + launchFromTaskbarPreservingSplitIfVisible( + recents, view, Collections.singletonList(info)); } } catch (NullPointerException @@ -1050,7 +1062,21 @@ public class TaskbarActivityContext extends BaseTaskbarContext { Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e); return; } + } + // If the app was launched from a folder, stash the taskbar after it closes + Folder f = Folder.getOpen(this); + if (f != null && f.getInfo().id == info.container) { + f.addOnFolderStateChangedListener(new Folder.OnFolderStateChangedListener() { + @Override + public void onFolderStateChanged(int newState) { + if (newState == Folder.STATE_CLOSED) { + f.removeOnFolderStateChangedListener(this); + mControllers.taskbarStashController + .updateAndAnimateTransientTaskbar(true); + } + } + }); } mControllers.uiController.onTaskbarIconLaunched(info); mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); @@ -1058,14 +1084,13 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } else if (tag instanceof AppInfo) { // Tapping an item in AllApps AppInfo info = (AppInfo) tag; - TaskbarUIController taskbarUIController = mControllers.uiController; - RecentsView recents = taskbarUIController.getRecentsView(); if (recents != null && taskbarUIController.getRecentsView().isSplitSelectionActive()) { // If we are selecting a second app for split, launch the split tasks taskbarUIController.triggerSecondAppForSplit(info, info.intent, view); } else { - launchFromTaskbarPreservingSplitIfVisible(recents, info); + launchFromTaskbarPreservingSplitIfVisible( + recents, view, Collections.singletonList(info)); } mControllers.uiController.onTaskbarIconLaunched(info); mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); @@ -1087,17 +1112,22 @@ public class TaskbarActivityContext extends BaseTaskbarContext { * (potentially breaking a split pair). */ private void launchFromTaskbarPreservingSplitIfVisible(@Nullable RecentsView recents, - ItemInfo info) { + @Nullable View launchingIconView, List<? extends ItemInfo> itemInfos) { if (recents == null) { return; } + + boolean findExactPairMatch = itemInfos.size() == 2; + // Convert the list of ItemInfo instances to a list of ComponentKeys + List<ComponentKey> componentKeys = + itemInfos.stream().map(ItemInfo::getComponentKey).toList(); recents.getSplitSelectController().findLastActiveTasksAndRunCallback( - Collections.singletonList(info.getComponentKey()), + componentKeys, + findExactPairMatch, foundTasks -> { @Nullable Task foundTask = foundTasks.get(0); if (foundTask != null) { - TaskView foundTaskView = - recents.getTaskViewByTaskId(foundTask.key.id); + TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id); if (foundTaskView != null && foundTaskView.isVisibleToUser()) { TestLogging.recordEvent( @@ -1106,8 +1136,16 @@ public class TaskbarActivityContext extends BaseTaskbarContext { return; } } - startItemInfoActivity(info); - }); + + if (findExactPairMatch) { + // We did not find the app pair we were looking for, so launch one. + recents.getSplitSelectController().getAppPairsController().launchAppPair( + (AppPairIcon) launchingIconView); + } else { + startItemInfoActivity(itemInfos.get(0)); + } + } + ); } private void startItemInfoActivity(ItemInfo info) { @@ -1129,6 +1167,41 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } } + /** Expands a folder icon when it is clicked */ + private void expandFolder(FolderIcon folderIcon) { + Folder folder = folderIcon.getFolder(); + + folder.setPriorityOnFolderStateChangedListener( + new Folder.OnFolderStateChangedListener() { + @Override + public void onFolderStateChanged(int newState) { + if (newState == Folder.STATE_OPEN) { + setTaskbarWindowFocusableForIme(true); + } else if (newState == Folder.STATE_CLOSED) { + // Defer by a frame to ensure we're no longer fullscreen and thus + // won't jump. + getDragLayer().post(() -> setTaskbarWindowFocusableForIme(false)); + folder.setPriorityOnFolderStateChangedListener(null); + } + } + }); + + setTaskbarWindowFullscreen(true); + + getDragLayer().post(() -> { + folder.animateOpen(); + getStatsLogManager().logger().withItemInfo(folder.mInfo).log(LAUNCHER_FOLDER_OPEN); + + folder.iterateOverItems((itemInfo, itemView) -> { + mControllers.taskbarViewController + .setClickAndLongClickListenersForIcon(itemView); + // To play haptic when dragging, like other Taskbar items do. + itemView.setHapticFeedbackEnabled(true); + return false; + }); + }); + } + /** * Returns whether the taskbar is currently visually stashed. */ @@ -1137,15 +1210,6 @@ 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() { - return mControllers.taskbarStashController.onLongPressToUnstashTaskbar(); - } - - /** * Called when we want to unstash taskbar when user performs swipes up gesture. */ public void onSwipeToUnstashTaskbar() { @@ -1200,28 +1264,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { * @param animateForward Whether to animate towards the unstashed hint state or back to stashed. */ public void startTaskbarUnstashHint(boolean animateForward) { - // TODO(b/270395798): Clean up forceUnstash after removing long-press unstashing code. - startTaskbarUnstashHint(animateForward, /* forceUnstash = */ false); - } - - /** - * 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. - */ - public void startTaskbarUnstashHint(boolean animateForward, boolean forceUnstash) { - // TODO(b/270395798): Clean up forceUnstash after removing long-press unstashing code. - mControllers.taskbarStashController.startUnstashHint(animateForward, forceUnstash); - } - - /** - * Enables manual taskbar stashing. This method should only be used for tests that need to - * stash/unstash the taskbar. - */ - @VisibleForTesting - public void enableManualStashingDuringTests(boolean enableManualStashing) { - mControllers.taskbarStashController.enableManualStashingDuringTests(enableManualStashing); + mControllers.taskbarStashController.startUnstashHint(animateForward); } /** @@ -1234,15 +1277,12 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } /** - * Unstashes the Taskbar if it is stashed. This method should only be used to unstash the - * taskbar at the end of a test. + * Unstashes the Taskbar if it is stashed. */ @VisibleForTesting public void unstashTaskbarIfStashed() { if (DisplayController.isTransientTaskbar(this)) { mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false); - } else { - mControllers.taskbarStashController.onLongPressToUnstashTaskbar(); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java index ffaee455d9..333c07b8fa 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java @@ -22,7 +22,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBIL import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static com.android.launcher3.taskbar.NavbarButtonsViewController.ALPHA_INDEX_IMMERSIVE_MODE; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IMMERSIVE_MODE; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY; import android.os.Bundle; import android.os.Handler; @@ -84,7 +84,7 @@ public class TaskbarForceVisibleImmersiveController implements TouchController { /** Update values tracked via sysui flags. */ public void updateSysuiFlags(int sysuiFlags) { - mIsImmersiveMode = (sysuiFlags & SYSUI_STATE_IMMERSIVE_MODE) != 0; + mIsImmersiveMode = (sysuiFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) == 0; if (mContext.isNavBarForceVisible()) { if (mIsImmersiveMode) { startIconDimming(); @@ -158,8 +158,7 @@ public class TaskbarForceVisibleImmersiveController implements TouchController { @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { - if (!isNavbarShownInImmersiveMode() - || mControllers.taskbarStashController.supportsManualStashing()) { + if (!isNavbarShownInImmersiveMode()) { return false; } return onControllerTouchEvent(ev); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index 9a37bcb76d..057b71b74f 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -205,13 +205,6 @@ public class TaskbarLauncherStateController { public void onStateTransitionComplete(LauncherState finalState) { mLauncherState = finalState; 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 (!FeatureFlags.enableHomeTransitionListener() - && finalState == LauncherState.NORMAL - && mLauncher.hasBeenResumed()) { - updateStateForFlag(FLAG_VISIBLE, true); - } applyState(); boolean disallowLongClick = FeatureFlags.enableSplitContextually() @@ -223,6 +216,21 @@ public class TaskbarLauncherStateController { } }; + /** + * Callback for when launcher state transition completes after user swipes to home. + * @param finalState The final state of the transition. + */ + public void onStateTransitionCompletedAfterSwipeToHome(LauncherState finalState) { + // TODO(b/279514548) Cleans up bad state that can occur when user interacts with + // taskbar on top of transparent activity. + if (!FeatureFlags.enableHomeTransitionListener() + && (finalState == LauncherState.NORMAL) + && mLauncher.hasBeenResumed()) { + updateStateForFlag(FLAG_VISIBLE, true); + applyState(); + } + } + /** Initializes the controller instance, and applies the initial state immediately. */ public void init(TaskbarControllers controllers, QuickstepLauncher launcher, int sysuiStateFlags) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java index c0b07e7412..bbac11625d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -106,6 +106,7 @@ public class TaskbarManager { Settings.Secure.NAV_BAR_KIDS_MODE); private final Context mContext; + private final @Nullable Context mNavigationBarPanelContext; private WindowManager mWindowManager; private FrameLayout mTaskbarRootLayout; private boolean mAddedWindow; @@ -198,6 +199,9 @@ public class TaskbarManager { mContext = service.createWindowContext(display, ENABLE_TASKBAR_NAVBAR_UNIFICATION ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL, null); + mNavigationBarPanelContext = ENABLE_TASKBAR_NAVBAR_UNIFICATION + ? service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null) + : null; if (enableTaskbarNoRecreate()) { mWindowManager = mContext.getSystemService(WindowManager.class); mTaskbarRootLayout = new FrameLayout(mContext) { @@ -435,8 +439,9 @@ public class TaskbarManager { } if (enableTaskbarNoRecreate() || mTaskbarActivityContext == null) { - mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp, - mNavButtonController, mUnfoldProgressProvider); + mTaskbarActivityContext = new TaskbarActivityContext(mContext, + mNavigationBarPanelContext, dp, mNavButtonController, + mUnfoldProgressProvider); } else { mTaskbarActivityContext.updateDeviceProfile(dp); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt index cbfa0247e8..6cb28eee36 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt @@ -16,7 +16,9 @@ package com.android.launcher3.taskbar import android.animation.AnimatorSet +import android.annotation.SuppressLint import android.view.View +import androidx.annotation.VisibleForTesting import androidx.core.animation.doOnEnd import com.android.launcher3.LauncherPrefs import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING @@ -31,46 +33,68 @@ class TaskbarPinningController(private val context: TaskbarActivityContext) : private lateinit var controllers: TaskbarControllers private lateinit var taskbarSharedState: TaskbarSharedState - private val launcherPrefs = LauncherPrefs.get(context) + private lateinit var launcherPrefs: LauncherPrefs private val statsLogManager = context.statsLogManager - private var isAnimatingTaskbarPinning = false + @VisibleForTesting var isAnimatingTaskbarPinning = false + @VisibleForTesting lateinit var onCloseCallback: (preferenceChanged: Boolean) -> Unit + @SuppressLint("VisibleForTests") fun init(taskbarControllers: TaskbarControllers, sharedState: TaskbarSharedState) { controllers = taskbarControllers taskbarSharedState = sharedState + launcherPrefs = context.launcherPrefs + onCloseCallback = + fun(didPreferenceChange: Boolean) { + statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE) + context.dragLayer.post { context.onPopupVisibilityChanged(false) } + + if (!didPreferenceChange) { + return + } + val animateToValue = + if (!launcherPrefs.get(TASKBAR_PINNING)) { + PINNING_PERSISTENT + } else { + PINNING_TRANSIENT + } + taskbarSharedState.taskbarWasPinned = animateToValue == PINNING_TRANSIENT + animateTaskbarPinning(animateToValue) + } } fun showPinningView(view: View) { context.isTaskbarWindowFullscreen = true - view.post { - val popupView = createAndPopulate(view, context) + val popupView = getPopupView(view) popupView.requestFocus() - - popupView.onCloseCallback = - callback@{ didPreferenceChange -> - statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE) - context.dragLayer.post { context.onPopupVisibilityChanged(false) } - - if (!didPreferenceChange) { - return@callback - } - val animateToValue = - if (!launcherPrefs.get(TASKBAR_PINNING)) { - PINNING_PERSISTENT - } else { - PINNING_TRANSIENT - } - taskbarSharedState.taskbarWasPinned = animateToValue == PINNING_TRANSIENT - animateTaskbarPinning(animateToValue) - } + popupView.onCloseCallback = onCloseCallback context.onPopupVisibilityChanged(true) popupView.show() statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN) } } - private fun animateTaskbarPinning(animateToValue: Float) { + @VisibleForTesting + fun getPopupView(view: View): TaskbarDividerPopupView<*> { + return createAndPopulate(view, context) + } + + @VisibleForTesting + fun animateTaskbarPinning(animateToValue: Float) { + val taskbarViewController = controllers.taskbarViewController + val animatorSet = + getAnimatorSetForTaskbarPinningAnimation(animateToValue).apply { + doOnEnd { recreateTaskbarAndUpdatePinningValue() } + duration = PINNING_ANIMATION_DURATION + } + controllers.taskbarOverlayController.hideWindow() + updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(true) + taskbarViewController.animateAwayNotificationDotsDuringTaskbarPinningAnimation() + animatorSet.start() + } + + @VisibleForTesting + fun getAnimatorSetForTaskbarPinningAnimation(animateToValue: Float): AnimatorSet { val animatorSet = AnimatorSet() val taskbarViewController = controllers.taskbarViewController val dragLayerController = controllers.taskbarDragLayerController @@ -82,13 +106,7 @@ class TaskbarPinningController(private val context: TaskbarActivityContext) : taskbarViewController.taskbarIconTranslationXForPinning.animateToValue(animateToValue) ) - controllers.taskbarOverlayController.hideWindow() - - animatorSet.doOnEnd { recreateTaskbarAndUpdatePinningValue() } - animatorSet.duration = PINNING_ANIMATION_DURATION - updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(true) - taskbarViewController.animateAwayNotificationDotsDuringTaskbarPinningAnimation() - animatorSet.start() + return animatorSet } private fun updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(isAnimating: Boolean) { @@ -96,7 +114,8 @@ class TaskbarPinningController(private val context: TaskbarActivityContext) : context.dragLayer.setAnimatingTaskbarPinning(isAnimating) } - private fun recreateTaskbarAndUpdatePinningValue() { + @VisibleForTesting + fun recreateTaskbarAndUpdatePinningValue() { updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(false) launcherPrefs.put(TASKBAR_PINNING, !launcherPrefs.get(TASKBAR_PINNING)) } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java index 176a8c5e51..d09f74c65a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java @@ -73,9 +73,21 @@ public class TaskbarSharedState { }; // Allows us to shift translation logic when doing taskbar pinning animation. - public Boolean startTaskbarVariantIsTransient = true; + public boolean startTaskbarVariantIsTransient = true; // To track if taskbar was pinned using taskbar pinning feature at the time of recreate, // so we can unstash transient taskbar when we un-pinning taskbar. - public Boolean taskbarWasPinned = false; + private boolean mTaskbarWasPinned = false; + + public boolean getTaskbarWasPinned() { + return mTaskbarWasPinned; + } + + public void setTaskbarWasPinned(boolean taskbarWasPinned) { + mTaskbarWasPinned = taskbarWasPinned; + } + + // To track if taskbar was stashed / unstashed between configuration changes (which recreates + // the task bar). + public Boolean taskbarWasStashedAuto = true; } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java index e8c8fc49fc..bfbecf3f77 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java @@ -34,6 +34,7 @@ import com.android.launcher3.R; import com.android.launcher3.accessibility.BaseAccessibilityDelegate; import com.android.launcher3.logging.StatsLogManager; 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.notification.NotificationListener; import com.android.launcher3.util.ShortcutUtil; @@ -84,9 +85,9 @@ public class TaskbarShortcutMenuAccessibilityDelegate @Override protected boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) { - if (item instanceof WorkspaceItemInfo + if (item instanceof ItemInfoWithIcon && (action == MOVE_TO_TOP_OR_LEFT || action == MOVE_TO_BOTTOM_OR_RIGHT)) { - WorkspaceItemInfo info = (WorkspaceItemInfo) item; + ItemInfoWithIcon info = (ItemInfoWithIcon) item; int side = action == MOVE_TO_TOP_OR_LEFT ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT; @@ -97,10 +98,11 @@ public class TaskbarShortcutMenuAccessibilityDelegate .withInstanceId(instanceIds.second) .log(getLogEventForPosition(side)); - if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT + && item instanceof WorkspaceItemInfo) { SystemUiProxy.INSTANCE.get(mContext).startShortcut( info.getIntent().getPackage(), - info.getDeepShortcutId(), + ((WorkspaceItemInfo) info).getDeepShortcutId(), side, /* bundleOpts= */ null, info.user, diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index a34df4f0e0..9aaa80f74c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -15,17 +15,13 @@ */ 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; -import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning; -import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE; -import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW; +import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_HIDE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_SHOW; import static com.android.launcher3.taskbar.TaskbarKeyguardController.MASK_ANY_SYSUI_LOCKED; @@ -44,7 +40,6 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.app.RemoteAction; -import android.content.SharedPreferences; import android.graphics.drawable.Icon; import android.os.SystemClock; import android.util.Log; @@ -62,9 +57,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; import com.android.launcher3.Alarm; 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.anim.AnimatorListeners; import com.android.launcher3.util.DisplayController; @@ -86,27 +79,26 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba private static final boolean DEBUG = false; public static final int FLAG_IN_APP = 1 << 0; - public static final int FLAG_STASHED_IN_APP_MANUAL = 1 << 1; // long press, persisted - public static final int FLAG_STASHED_IN_APP_SYSUI = 1 << 2; // shade open, ... - public static final int FLAG_STASHED_IN_APP_SETUP = 1 << 3; // setup wizard and AllSetActivity - public static final int FLAG_STASHED_IN_APP_IME = 1 << 4; // IME is visible - public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 5; - public static final int FLAG_STASHED_IN_TASKBAR_ALL_APPS = 1 << 6; // All apps is visible. - public static final int FLAG_IN_SETUP = 1 << 7; // In the Setup Wizard - public static final int FLAG_STASHED_SMALL_SCREEN = 1 << 8; // phone screen gesture nav, stashed - public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 9; // Autohide (transient taskbar). - public static final int FLAG_STASHED_SYSUI = 1 << 10; // app pinning,... - public static final int FLAG_STASHED_DEVICE_LOCKED = 1 << 11; // device is locked: keyguard, ... - public static final int FLAG_IN_OVERVIEW = 1 << 12; // launcher is in overview + public static final int FLAG_STASHED_IN_APP_SYSUI = 1 << 1; // shade open, ... + public static final int FLAG_STASHED_IN_APP_SETUP = 1 << 2; // setup wizard and AllSetActivity + public static final int FLAG_STASHED_IN_APP_IME = 1 << 3; // IME is visible + public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 4; + public static final int FLAG_STASHED_IN_TASKBAR_ALL_APPS = 1 << 5; // All apps is visible. + public static final int FLAG_IN_SETUP = 1 << 6; // In the Setup Wizard + public static final int FLAG_STASHED_SMALL_SCREEN = 1 << 7; // phone screen gesture nav, stashed + public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 8; // Autohide (transient taskbar). + public static final int FLAG_STASHED_SYSUI = 1 << 9; // app pinning,... + public static final int FLAG_STASHED_DEVICE_LOCKED = 1 << 10; // device is locked: keyguard, ... + public static final int FLAG_IN_OVERVIEW = 1 << 11; // launcher is in overview // If any of these flags are enabled, isInApp should return true. private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP; // If we're in an app and any of these flags are enabled, taskbar should be stashed. - private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL - | FLAG_STASHED_IN_APP_SYSUI | FLAG_STASHED_IN_APP_SETUP - | FLAG_STASHED_IN_APP_IME | FLAG_STASHED_IN_TASKBAR_ALL_APPS - | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO; + private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_SYSUI + | FLAG_STASHED_IN_APP_SETUP | FLAG_STASHED_IN_APP_IME + | FLAG_STASHED_IN_TASKBAR_ALL_APPS | FLAG_STASHED_SMALL_SCREEN + | FLAG_STASHED_IN_APP_AUTO; // If any of these flags are enabled, inset apps by our stashed height instead of our unstashed // height. This way the reported insets are consistent even during transitions out of the app. @@ -167,21 +159,11 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba private static final long TASKBAR_STASH_ICON_ALPHA_HOME_TO_APP_START_DELAY = 66; /** - * The scale that TaskbarView animates to when hinting towards the stashed state. - */ - private static final float STASHED_TASKBAR_HINT_SCALE = 0.9f; - - /** * The scale that the stashed handle animates to when hinting towards the unstashed state. */ private static final float UNSTASHED_TASKBAR_HANDLE_HINT_SCALE = 1.1f; /** - * The SharedPreferences key for whether user has manually stashed the taskbar. - */ - private static final String SHARED_PREFS_STASHED_KEY = "taskbar_is_stashed"; - - /** * Whether taskbar should be stashed out of the box. */ private static final boolean DEFAULT_STASHED_PREF = false; @@ -224,7 +206,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba private @interface StashAnimation {} private final TaskbarActivityContext mActivity; - private final SharedPreferences mPrefs; private final int mStashedHeight; private final int mUnstashedHeight; private final SystemUiProxy mSystemUiProxy; @@ -253,8 +234,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba private boolean mIsImeShowing; private boolean mIsImeSwitcherShowing; - private boolean mEnableManualStashingDuringTests = false; - private final Alarm mTimeoutAlarm = new Alarm(); private boolean mEnableBlockingTimeoutDuringTests = false; @@ -274,12 +253,12 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba public TaskbarStashController(TaskbarActivityContext activity) { mActivity = activity; - mPrefs = LauncherPrefs.getPrefs(mActivity); mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity); mAccessibilityManager = mActivity.getSystemService(AccessibilityManager.class); if (isPhoneMode()) { - mUnstashedHeight = mActivity.getResources().getDimensionPixelSize(R.dimen.taskbar_size); + mUnstashedHeight = mActivity.getResources().getDimensionPixelSize( + R.dimen.taskbar_phone_size); mStashedHeight = mActivity.getResources().getDimensionPixelSize( R.dimen.taskbar_stashed_size); } else { @@ -328,17 +307,13 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale(); boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity); - // We use supportsVisualStashing() here instead of supportsManualStashing() because we want - // it to work properly for tests that recreate taskbar. This check is here just to ensure - // that taskbar unstashes when going to 3 button mode (supportsVisualStashing() false). - boolean isManuallyStashedInApp = supportsVisualStashing() - && !isTransientTaskbar - && !enableTaskbarPinning() - && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF); boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible; - updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp); - updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, - isTransientTaskbar && !mTaskbarSharedState.taskbarWasPinned); + boolean isStashedInAppAuto = + isTransientTaskbar && !mTaskbarSharedState.getTaskbarWasPinned(); + if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) { + isStashedInAppAuto = isStashedInAppAuto && mTaskbarSharedState.taskbarWasStashedAuto; + } + updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, isStashedInAppAuto); updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup); updateStateForFlag(FLAG_IN_SETUP, isInSetup); updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, isPhoneMode() @@ -347,7 +322,8 @@ 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); - if (mTaskbarSharedState.taskbarWasPinned) { + if (mTaskbarSharedState.getTaskbarWasPinned() + || !mTaskbarSharedState.taskbarWasStashedAuto) { tryStartTaskbarTimeout(); } notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp()); @@ -362,28 +338,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } /** - * Returns whether the user can manually stash the taskbar based on the current device state. - */ - protected boolean supportsManualStashing() { - if (enableTaskbarPinning() && LauncherPrefs.get(mActivity).get(TASKBAR_PINNING)) { - return false; - } - return supportsVisualStashing() - && isInApp() - && (!Utilities.isRunningInTestHarness() || mEnableManualStashingDuringTests) - && !DisplayController.isTransientTaskbar(mActivity); - } - - /** - * Enables support for manual stashing. This should only be used to add this functionality - * to Launcher specific tests. - */ - @VisibleForTesting - public void enableManualStashingDuringTests(boolean enableManualStashing) { - mEnableManualStashingDuringTests = enableManualStashing; - } - - /** * Enables the auto timeout for taskbar stashing. This method should only be used for taskbar * testing. */ @@ -544,6 +498,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } if (hasAnyFlag(FLAG_STASHED_IN_APP_AUTO) != stash) { + mTaskbarSharedState.taskbarWasStashedAuto = stash; updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, stash); applyState(); } @@ -577,53 +532,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba /* shouldBubblesFollow= */ !bubbleBarExpanded); } - /** - * Should be called when long pressing the nav region when taskbar is present. - * @return Whether taskbar was stashed and now is unstashed. - */ - public boolean onLongPressToUnstashTaskbar() { - if (!isStashed()) { - // We only listen for long press on the nav region to unstash the taskbar. To stash the - // taskbar, we use an OnLongClickListener on TaskbarView instead. - return false; - } - if (!canCurrentlyManuallyUnstash()) { - return false; - } - if (updateAndAnimateIsManuallyStashedInApp(false)) { - mControllers.taskbarActivityContext.getDragLayer().performHapticFeedback(LONG_PRESS); - return true; - } - return false; - } - - /** - * Returns whether taskbar will unstash when long pressing it based on the current state. The - * only time this is true is if the user is in an app and the taskbar is only stashed because - * the user previously long pressed to manually stash (not due to other reasons like IME). - */ - private boolean canCurrentlyManuallyUnstash() { - return (mState & (FLAG_IN_APP | FLAGS_STASHED_IN_APP)) - == (FLAG_IN_APP | FLAG_STASHED_IN_APP_MANUAL); - } - - /** - * Updates whether we should stash the taskbar when in apps, and animates to the changed state. - * @return Whether we started an animation to either be newly stashed or unstashed. - */ - public boolean updateAndAnimateIsManuallyStashedInApp(boolean isManuallyStashedInApp) { - if (!supportsManualStashing()) { - return false; - } - if (hasAnyFlag(FLAG_STASHED_IN_APP_MANUAL) != isManuallyStashedInApp) { - mPrefs.edit().putBoolean(SHARED_PREFS_STASHED_KEY, isManuallyStashedInApp).apply(); - updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp); - applyState(); - return true; - } - return false; - } - /** Toggles the Taskbar's stash state. */ public void toggleTaskbarStash() { if (!DisplayController.isTransientTaskbar(mActivity) || !hasAnyFlag(FLAGS_IN_APP)) return; @@ -910,21 +818,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } }); } - /** - * Creates and starts a partial stash animation, hinting at the new state that will trigger when - * long press is detected. - * @param animateForward Whether we are going towards the new stashed state or returning to the - * unstashed state. - */ - public void startStashHint(boolean animateForward) { - if (isStashed() || !supportsManualStashing()) { - // Already stashed, no need to hint in that direction. - return; - } - mIconScaleForStash.animateToValue( - animateForward ? STASHED_TASKBAR_HINT_SCALE : 1) - .setDuration(TASKBAR_HINT_STASH_DURATION).start(); - } /** * Creates and starts a partial unstash animation, hinting at the new state that will trigger @@ -932,19 +825,12 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba * * @param animateForward Whether we are going towards the new unstashed state or returning to * the stashed state. - * @param forceUnstash Whether we force the unstash hint to animate. */ - protected void startUnstashHint(boolean animateForward, boolean forceUnstash) { + protected void startUnstashHint(boolean animateForward) { if (!isStashed()) { // Already unstashed, no need to hint in that direction. return; } - // TODO(b/270395798): Clean up after removing long-press unstashing code path. - if (!canCurrentlyManuallyUnstash() && !forceUnstash) { - // If any other flags are causing us to be stashed, long press won't cause us to - // unstash, so don't hint that it will. - return; - } mTaskbarStashedHandleHintScale.animateToValue( animateForward ? UNSTASHED_TASKBAR_HANDLE_HINT_SCALE : 1) .setDuration(TASKBAR_HINT_STASH_DURATION).start(); @@ -1097,13 +983,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba mControllers.taskbarAutohideSuspendController.updateFlag( TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER, !isInApp()); } - if (hasAnyFlag(changedFlags, FLAG_STASHED_IN_APP_MANUAL)) { - if (hasAnyFlag(FLAG_STASHED_IN_APP_MANUAL)) { - mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_LONGPRESS_HIDE); - } else { - mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_LONGPRESS_SHOW); - } - } if (hasAnyFlag(changedFlags, FLAG_STASHED_IN_APP_AUTO)) { mActivity.getStatsLogManager().logger().log(hasAnyFlag(FLAG_STASHED_IN_APP_AUTO) ? LAUNCHER_TRANSIENT_TASKBAR_HIDE @@ -1227,7 +1106,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba private static String getStateString(int flags) { StringJoiner sj = new StringJoiner("|"); appendFlag(sj, flags, FLAGS_IN_APP, "FLAG_IN_APP"); - appendFlag(sj, flags, FLAG_STASHED_IN_APP_MANUAL, "FLAG_STASHED_IN_APP_MANUAL"); appendFlag(sj, flags, FLAG_STASHED_IN_APP_SYSUI, "FLAG_STASHED_IN_APP_SYSUI"); appendFlag(sj, flags, FLAG_STASHED_IN_APP_SETUP, "FLAG_STASHED_IN_APP_SETUP"); appendFlag(sj, flags, FLAG_STASHED_IN_APP_IME, "FLAG_STASHED_IN_APP_IME"); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index 445b312424..df2a43b170 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -30,6 +30,7 @@ import androidx.annotation.CallSuper; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; @@ -216,6 +217,7 @@ public class TaskbarUIController { recentsView.getSplitSelectController().findLastActiveTasksAndRunCallback( Collections.singletonList(splitSelectSource.itemInfo.getComponentKey()), + false /* findExactPairMatch */, foundTasks -> { @Nullable Task foundTask = foundTasks.get(0); splitSelectSource.alreadyRunningTaskId = foundTask == null @@ -234,6 +236,7 @@ public class TaskbarUIController { RecentsView recents = getRecentsView(); recents.getSplitSelectController().findLastActiveTasksAndRunCallback( Collections.singletonList(info.getComponentKey()), + false /* findExactPairMatch */, foundTasks -> { @Nullable Task foundTask = foundTasks.get(0); if (foundTask != null) { @@ -297,11 +300,9 @@ public class TaskbarUIController { } /** - * Launches the focused task in splitscreen. - * - * No-op if the view is not yet open. + * Launches the given task in split-screen. */ - public void launchSplitTasks(@NonNull View taskview, @NonNull GroupTask groupTask) { } + public void launchSplitTasks(@NonNull GroupTask groupTask) { } /** * Returns the matching view (if any) in the taskbar. @@ -330,6 +331,14 @@ public class TaskbarUIController { } /** + * Callback for when launcher state transition completes after user swipes to home. + * @param finalState The final state of the transition. + */ + public void onStateTransitionCompletedAfterSwipeToHome(LauncherState finalState) { + // Overridden + } + + /** * Refreshes the resumed state of this ui controller. */ public void refreshResumedState() {} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index 1be17989f1..2ab00665ed 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -19,6 +19,8 @@ import static android.content.pm.PackageManager.FEATURE_PC; import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED; import static com.android.launcher3.Flags.enableCursorHoverStates; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER; import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR; import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning; import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR; @@ -47,6 +49,7 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.apppairs.AppPairIcon; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.icons.ThemedIconDrawable; import com.android.launcher3.model.data.FolderInfo; @@ -261,8 +264,6 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar mIconClickListener = mControllerCallbacks.getIconOnClickListener(); mIconLongClickListener = mControllerCallbacks.getIconOnLongClickListener(); - setOnLongClickListener(mControllerCallbacks.getBackgroundOnLongClickListener()); - if (mAllAppsButton != null) { mAllAppsButton.setOnClickListener(mControllerCallbacks.getAllAppsButtonClickListener()); } @@ -309,12 +310,14 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar // Replace any Hotseat views with the appropriate type if it's not already that type. final int expectedLayoutResId; - boolean isFolder = false; + boolean isCollection = false; if (hotseatItemInfo.isPredictedItem()) { expectedLayoutResId = R.layout.taskbar_predicted_app_icon; - } else if (hotseatItemInfo instanceof FolderInfo) { - expectedLayoutResId = R.layout.folder_icon; - isFolder = true; + } else if (hotseatItemInfo instanceof FolderInfo fi) { + expectedLayoutResId = fi.itemType == ITEM_TYPE_APP_PAIR + ? R.layout.app_pair_icon + : R.layout.folder_icon; + isCollection = true; } else { expectedLayoutResId = R.layout.taskbar_app_icon; } @@ -325,7 +328,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar // see if the view can be reused if ((hotseatView.getSourceLayoutResId() != expectedLayoutResId) - || (isFolder && (hotseatView.getTag() != hotseatItemInfo))) { + || (isCollection && (hotseatView.getTag() != hotseatItemInfo))) { // Unlike for BubbleTextView, we can't reapply a new FolderInfo after inflation, // so if the info changes we need to reinflate. This should only happen if a new // folder is dragged to the position that another folder previously existed. @@ -338,12 +341,23 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar } if (hotseatView == null) { - if (isFolder) { + if (isCollection) { FolderInfo folderInfo = (FolderInfo) hotseatItemInfo; - FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId, - mActivityContext, this, folderInfo); - folderIcon.setTextVisible(false); - hotseatView = folderIcon; + switch (hotseatItemInfo.itemType) { + case ITEM_TYPE_FOLDER: + hotseatView = FolderIcon.inflateFolderAndIcon( + expectedLayoutResId, mActivityContext, this, folderInfo); + ((FolderIcon) hotseatView).setTextVisible(false); + break; + case ITEM_TYPE_APP_PAIR: + hotseatView = AppPairIcon.inflateIcon( + expectedLayoutResId, mActivityContext, this, folderInfo); + ((AppPairIcon) hotseatView).setTextVisible(false); + break; + default: + throw new IllegalStateException( + "Unexpected item type: " + hotseatItemInfo.itemType); + } } else { hotseatView = inflate(expectedLayoutResId); } @@ -513,24 +527,6 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar } } - @Override - public boolean onTouchEvent(MotionEvent event) { - if (mIconLayoutBounds.left <= event.getX() && event.getX() <= mIconLayoutBounds.right) { - // Don't allow long pressing between icons, or above/below them. - return true; - } - if (mControllerCallbacks.onTouchEvent(event)) { - int oldAction = event.getAction(); - try { - event.setAction(MotionEvent.ACTION_CANCEL); - return super.onTouchEvent(event); - } finally { - event.setAction(oldAction); - } - } - return super.onTouchEvent(event); - } - /** * Returns whether the given MotionEvent, *in screen coorindates*, is within any Taskbar item's * touch bounds. diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 8a7a98cef0..c0cbd45cc1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -22,7 +22,6 @@ 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; import static com.android.launcher3.Utilities.mapRange; -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.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION; @@ -193,7 +192,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar mControllers = controllers; mTaskbarView.init(new TaskbarViewCallbacks()); mTaskbarView.getLayoutParams().height = isPhoneMode(mActivity.getDeviceProfile()) - ? mActivity.getResources().getDimensionPixelSize(R.dimen.taskbar_size) + ? mActivity.getResources().getDimensionPixelSize(R.dimen.taskbar_phone_size) : mActivity.getDeviceProfile().taskbarHeight; mTaskbarIconScaleForStash.updateValue(1f); @@ -899,53 +898,12 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar return mControllers.taskbarDragController::startDragOnLongClick; } - public View.OnLongClickListener getBackgroundOnLongClickListener() { - return view -> mControllers.taskbarStashController - .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. - * TODO(b/270395798): We can remove this entirely once we remove the Transient Taskbar flag. - */ - public boolean onTouchEvent(MotionEvent motionEvent) { - final float x = motionEvent.getRawX(); - final float y = motionEvent.getRawY(); - switch (motionEvent.getAction()) { - case MotionEvent.ACTION_DOWN: - mDownX = x; - mDownY = y; - mControllers.taskbarStashController.startStashHint(/* animateForward = */ true); - mCanceledStashHint = false; - break; - case MotionEvent.ACTION_MOVE: - if (!mCanceledStashHint - && squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) { - mControllers.taskbarStashController.startStashHint( - /* animateForward= */ false); - mCanceledStashHint = true; - return true; - } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - if (!mCanceledStashHint) { - mControllers.taskbarStashController.startStashHint( - /* animateForward= */ false); - } - break; - } - - return false; - } - - /** * Notifies launcher to update icon alignment. */ public void notifyIconLayoutBoundsChanged() { diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java index 5ce2a7a246..964d329066 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java @@ -222,7 +222,7 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverla if (ev.getAction() == MotionEvent.ACTION_DOWN) { mNoIntercept = !mAppsView.shouldContainerScroll(ev) || getTopOpenViewWithType( - mActivityContext, TYPE_ACCESSIBLE & ~TYPE_TASKBAR_OVERLAYS) != null; + mActivityContext, TYPE_TOUCH_CONTROLLER_NO_INTERCEPT) != null; } return super.onControllerInterceptTouchEvent(ev); } diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java index c4eeea7e9b..adbec65ad3 100644 --- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java +++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java @@ -16,6 +16,7 @@ package com.android.launcher3.taskbar.overlay; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_CONSUME_IME_INSETS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; @@ -187,6 +188,7 @@ public final class TaskbarOverlayController { layoutParams.setFitInsetsTypes(0); // Handled by container view. layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; layoutParams.setSystemApplicationOverlay(true); + layoutParams.privateFlags = PRIVATE_FLAG_CONSUME_IME_INSETS; return layoutParams; } diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 5b0c8c3ff1..9438e00b43 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -19,6 +19,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; 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.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO; import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE; @@ -34,8 +35,6 @@ 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.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; -import static com.android.launcher3.config.FeatureFlags.ENABLE_HOME_TRANSITION_LISTENER; -import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE; 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; @@ -107,6 +106,7 @@ import com.android.launcher3.Workspace; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; +import com.android.launcher3.apppairs.AppPairIcon; import com.android.launcher3.appprediction.PredictionRowView; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.desktop.DesktopRecentsTransitionController; @@ -117,7 +117,6 @@ 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.statehandlers.DepthController; @@ -643,6 +642,7 @@ public class QuickstepLauncher extends Launcher { // using that. mSplitSelectStateController.findLastActiveTasksAndRunCallback( Collections.singletonList(splitSelectSource.itemInfo.getComponentKey()), + false /* findExactPairMatch */, foundTasks -> { @Nullable Task foundTask = foundTasks.get(0); boolean taskWasFound = foundTask != null; @@ -707,6 +707,13 @@ public class QuickstepLauncher extends Launcher { } @Override + public void onStateTransitionCompletedAfterSwipeToHome(LauncherState finalState) { + if (mTaskbarUIController != null) { + mTaskbarUIController.onStateTransitionCompletedAfterSwipeToHome(finalState); + } + } + + @Override protected void onResume() { super.onResume(); @@ -1263,24 +1270,19 @@ public class QuickstepLauncher extends Launcher { /** * Launches the given {@link GroupTask} in splitscreen. - * - * If the second split task is missing, launches the first task normally. */ - public void launchSplitTasks(@NonNull View taskView, @NonNull GroupTask groupTask) { - if (groupTask.task2 == null) { - UI_HELPER_EXECUTOR.execute(() -> - ActivityManagerWrapper.getInstance().startActivityFromRecents( - groupTask.task1.key, - getActivityLaunchOptions(taskView, null).options)); - return; - } + public void launchSplitTasks(@NonNull GroupTask groupTask) { + // Top/left and bottom/right tasks respectively. + Task task1 = groupTask.task1; + // task2 should never be null when calling this method. Allow a crash to catch invalid calls + Task task2 = groupTask.task2; mSplitSelectStateController.launchExistingSplitPair( null /* launchingTaskView */, - groupTask.task1.key.id, - groupTask.task2.key.id, + task1.key.id, + task2.key.id, SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, /* callback= */ success -> mSplitSelectStateController.resetState(), - /* freezeTaskList= */ true, + /* freezeTaskList= */ false, groupTask.mSplitBounds == null ? SNAP_TO_50_50 : groupTask.mSplitBounds.snapPosition); @@ -1289,8 +1291,8 @@ public class QuickstepLauncher extends Launcher { /** * Launches two apps as an app pair. */ - public void launchAppPair(WorkspaceItemInfo app1, WorkspaceItemInfo app2) { - mSplitSelectStateController.getAppPairsController().launchAppPair(app1, app2); + public void launchAppPair(AppPairIcon appPairIcon) { + mSplitSelectStateController.getAppPairsController().launchAppPair(appPairIcon); } public boolean canStartHomeSafely() { diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java index 301fbe491b..c1a85fa71d 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java +++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java @@ -28,6 +28,7 @@ import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_H import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT; import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE; import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_TIMEOUT_MS; +import static com.android.launcher3.LauncherPrefs.PRIVATE_SPACE_APPS; import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_HIGHLIGHT_KEY; import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED; import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey; @@ -67,6 +68,7 @@ import androidx.preference.SeekBarPreference; import androidx.preference.SwitchPreference; import com.android.launcher3.ConstantItem; +import com.android.launcher3.Flags; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.R; import com.android.launcher3.config.FeatureFlags; @@ -115,6 +117,9 @@ public class DeveloperOptionsUI { addAllAppsFromOverviewCatergory(); } addCustomLpnhCategory(); + if (Flags.enablePrivateSpace()) { + addCustomPrivateAppsCategory(); + } } private void filterPreferences(String query, PreferenceGroup pg) { @@ -365,6 +370,12 @@ public class DeveloperOptionsUI { } } + private void addCustomPrivateAppsCategory() { + PreferenceCategory category = newCategory("Apps in Private Space Config"); + category.addPreference(createSeekBarPreference( + "Number of Apps to put in private region", 0, 100, 1, PRIVATE_SPACE_APPS)); + } + /** * Create a preference with text and a seek bar. Should be added to a PreferenceCategory. * diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java index f6cd30a222..82a9c058df 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -45,6 +45,7 @@ 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.statemanager.StateManager; import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.TouchController; @@ -194,7 +195,20 @@ public class NavBarToHomeTouchController implements TouchController, recentsView.switchToScreenshot(null, () -> recentsView.finishRecentsAnimation(true /* toRecents */, null)); if (mStartState.overviewUi) { - new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(mEndState), + Runnable onReachedHome = () -> { + StateManager.StateListener<LauncherState> listener = + new StateManager.StateListener<>() { + @Override + public void onStateTransitionComplete(LauncherState finalState) { + mLauncher.onStateTransitionCompletedAfterSwipeToHome( + finalState); + mLauncher.getStateManager().removeStateListener(this); + } + }; + mLauncher.getStateManager().addStateListener(listener); + onSwipeInteractionCompleted(mEndState); + }; + new OverviewToHomeAnim(mLauncher, onReachedHome, FeatureFlags.enableSplitContextually() ? mCancelSplitRunnable : null) diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java index 8cbf2394ae..2c937b008e 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java @@ -15,8 +15,7 @@ */ package com.android.launcher3.uioverrides.touchcontrollers; -import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE; -import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU; +import static com.android.launcher3.AbstractFloatingView.TYPE_TOUCH_CONTROLLER_NO_INTERCEPT; import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; @@ -84,7 +83,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr return false; } } - if (getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE | TYPE_ALL_APPS_EDU) != null) { + if (getTopOpenViewWithType(mLauncher, TYPE_TOUCH_CONTROLLER_NO_INTERCEPT) != null) { return false; } return true; diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java index 3d94857848..19bfe069c8 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java @@ -15,7 +15,7 @@ */ package com.android.launcher3.uioverrides.touchcontrollers; -import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE; +import static com.android.launcher3.AbstractFloatingView.TYPE_TOUCH_CONTROLLER_NO_INTERCEPT; import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH; @@ -112,7 +112,8 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity> // If we are already animating from a previous state, we can intercept. return true; } - if (AbstractFloatingView.getTopOpenViewWithType(mActivity, TYPE_ACCESSIBLE) != null) { + if (AbstractFloatingView.getTopOpenViewWithType( + mActivity, TYPE_TOUCH_CONTROLLER_NO_INTERCEPT) != null) { return false; } return isRecentsInteractive(); diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java index 5568459018..7c24ba8fe6 100644 --- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java +++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java @@ -23,6 +23,8 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; import static com.android.launcher3.BaseActivity.INVISIBLE_ALL; import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS; import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION; +import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING; +import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -52,9 +54,11 @@ import android.window.IOnBackInvokedCallback; import com.android.internal.view.AppearanceRegion; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.LauncherPrefs; import com.android.launcher3.QuickstepTransitionManager; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.taskbar.LauncherTaskbarUIController; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.quickstep.util.RectFSpringAnim; import com.android.systemui.shared.system.QuickStepContract; @@ -293,8 +297,12 @@ public class LauncherBackAnimationController { mBackTarget = appTarget; mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY()); - // TODO(b/218916755): Offset start rectangle in multiwindow mode. mStartRect.set(appTarget.windowConfiguration.getMaxBounds()); + if (mLauncher.getDeviceProfile().isTaskbarPresent && enableTaskbarPinning() + && LauncherPrefs.get(mLauncher).get(TASKBAR_PINNING)) { + int insetBottom = mStartRect.bottom - appTarget.contentInsets.bottom; + mStartRect.set(mStartRect.left, mStartRect.top, mStartRect.right, insetBottom); + } mCurrentRect.set(mStartRect); addScrimLayer(); mTransaction.apply(); @@ -416,6 +424,10 @@ public class LauncherBackAnimationController { if (mLauncher.isDestroyed()) { return; } + LauncherTaskbarUIController taskbarUIController = mLauncher.getTaskbarUIController(); + if (taskbarUIController != null) { + taskbarUIController.onLauncherVisibilityChanged(true); + } // TODO: Catch the moment when launcher becomes visible after the top app un-occludes // launcher and start animating afterwards. Currently we occasionally get a flicker from // animating when launcher is still invisible. diff --git a/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt b/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt new file mode 100644 index 0000000000..645ecf41a1 --- /dev/null +++ b/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt @@ -0,0 +1,136 @@ +package com.android.quickstep + +import android.app.backup.BackupManager +import android.app.backup.BackupRestoreEventLogger +import android.app.backup.BackupRestoreEventLogger.BackupRestoreDataType +import android.app.backup.BackupRestoreEventLogger.BackupRestoreError +import android.content.Context +import com.android.launcher3.Flags +import com.android.launcher3.LauncherSettings.Favorites +import com.android.launcher3.backuprestore.LauncherRestoreEventLogger + +/** + * Concrete implementation for wrapper to log Restore event metrics for both success and failure to + * restore Launcher workspace from a backup. This implementation accesses SystemApis so is only + * available to QuickStep/NexusLauncher. + */ +class LauncherRestoreEventLoggerImpl(val context: Context) : LauncherRestoreEventLogger() { + companion object { + const val TAG = "LauncherRestoreEventLoggerImpl" + + // Generic type for any possible workspace items, when specific type is not known. + @BackupRestoreDataType private const val DATA_TYPE_LAUNCHER_ITEM = "launcher_item" + // Specific workspace item types, based off of Favorites Table. + @BackupRestoreDataType private const val DATA_TYPE_APPLICATION = "application" + @BackupRestoreDataType private const val DATA_TYPE_FOLDER = "folder" + @BackupRestoreDataType private const val DATA_TYPE_APPWIDGET = "widget" + @BackupRestoreDataType private const val DATA_TYPE_CUSTOM_APPWIDGET = "custom_widget" + @BackupRestoreDataType private const val DATA_TYPE_DEEP_SHORTCUT = "deep_shortcut" + @BackupRestoreDataType private const val DATA_TYPE_APP_PAIR = "app_pair" + } + + private val backupManager: BackupManager = BackupManager(context) + private val restoreEventLogger: BackupRestoreEventLogger = backupManager.delayedRestoreLogger + + /** + * For logging when multiple items of a given data type failed to restore. + * + * @param dataType The data type that was not restored. + * @param count the number of data items that were not restored. + * @param error error type for why the data was not restored. + */ + override fun logLauncherItemsRestoreFailed( + @BackupRestoreDataType dataType: String, + count: Int, + @BackupRestoreError error: String? + ) { + if (Flags.enableLauncherBrMetrics()) { + restoreEventLogger.logItemsRestoreFailed(dataType, count, error) + } + } + + /** + * For logging when multiple items of a given data type were successfully restored. + * + * @param dataType The data type that was restored. + * @param count the number of data items restored. + */ + override fun logLauncherItemsRestored(@BackupRestoreDataType dataType: String, count: Int) { + if (Flags.enableLauncherBrMetrics()) { + restoreEventLogger.logItemsRestored(dataType, count) + } + } + + /** + * Helper to log successfully restoring a single item from the Favorites table. + * + * @param favoritesId The id of the item type from [Favorites] that was restored. + */ + override fun logSingleFavoritesItemRestored(favoritesId: Int) { + if (Flags.enableLauncherBrMetrics()) { + restoreEventLogger.logItemsRestored(favoritesIdToDataType(favoritesId), 1) + } + } + + /** + * Helper to log a failure to restore a single item from the Favorites table. + * + * @param favoritesId The id of the item type from [Favorites] that was not restored. + * @param error error type for why the data was not restored. + */ + override fun logSingleFavoritesItemRestoreFailed( + favoritesId: Int, + @BackupRestoreError error: String? + ) { + if (Flags.enableLauncherBrMetrics()) { + restoreEventLogger.logItemsRestoreFailed(favoritesIdToDataType(favoritesId), 1, error) + } + } + + /** + * Helper to log a failure to restore items from the Favorites table. + * + * @param favoritesId The id of the item type from [Favorites] that was not restored. + * @param count number of items that failed to restore. + * @param error error type for why the data was not restored. + */ + override fun logFavoritesItemsRestoreFailed( + favoritesId: Int, + count: Int, + @BackupRestoreError error: String? + ) { + if (Flags.enableLauncherBrMetrics()) { + restoreEventLogger.logItemsRestoreFailed( + favoritesIdToDataType(favoritesId), + count, + error + ) + } + } + + /** + * Uses the current [restoreEventLogger] to report its results to the [backupManager]. Use when + * done restoring items for Launcher. + */ + override fun reportLauncherRestoreResults() { + if (Flags.enableLauncherBrMetrics()) { + backupManager.reportDelayedRestoreResult(restoreEventLogger) + } + } + + /** + * Helper method to convert item types from [Favorites] to B&R data types for logging. Also to + * avoid direct usage of @BackupRestoreDataType which is protected under @SystemApi. + */ + @BackupRestoreDataType + private fun favoritesIdToDataType(favoritesId: Int): String = + when (favoritesId) { + Favorites.ITEM_TYPE_APPLICATION -> DATA_TYPE_APPLICATION + Favorites.ITEM_TYPE_FOLDER -> DATA_TYPE_FOLDER + Favorites.ITEM_TYPE_APPWIDGET -> DATA_TYPE_APPWIDGET + Favorites.ITEM_TYPE_CUSTOM_APPWIDGET -> DATA_TYPE_CUSTOM_APPWIDGET + Favorites.ITEM_TYPE_DEEP_SHORTCUT -> DATA_TYPE_DEEP_SHORTCUT + Favorites.ITEM_TYPE_APP_PAIR -> DATA_TYPE_APP_PAIR + else -> DATA_TYPE_LAUNCHER_ITEM + } +} diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java index 221ce480f8..03037914af 100644 --- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java +++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java @@ -82,34 +82,18 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { return response; } - case TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING: - runOnTISBinder(tisBinder -> { - enableManualTaskbarStashing(tisBinder, true); - }); - return response; - - case TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING: - runOnTISBinder(tisBinder -> { - enableManualTaskbarStashing(tisBinder, false); - }); - return response; - case TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED: runOnTISBinder(tisBinder -> { - enableManualTaskbarStashing(tisBinder, true); - // Allow null-pointer to catch illegal states. tisBinder.getTaskbarManager().getCurrentActivityContext() .unstashTaskbarIfStashed(); - - enableManualTaskbarStashing(tisBinder, false); }); return response; - case TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT: { + case TestProtocol.REQUEST_TASKBAR_FROM_NAV_THRESHOLD: { final Resources resources = mContext.getResources(); response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, - resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size)); + resources.getDimensionPixelSize(R.dimen.taskbar_from_nav_threshold)); return response; } @@ -187,13 +171,6 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { return super.isLauncherInitialized() && TouchInteractionService.isInitialized(); } - private void enableManualTaskbarStashing( - TouchInteractionService.TISBinder tisBinder, boolean enable) { - // Allow null-pointer to catch illegal states. - tisBinder.getTaskbarManager().getCurrentActivityContext().enableManualStashingDuringTests( - enable); - } - private void enableBlockingTimeout( TouchInteractionService.TISBinder tisBinder, boolean enable) { TaskbarActivityContext context = tisBinder.getTaskbarManager().getCurrentActivityContext(); diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index 39edd70ed7..22163b9614 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -18,9 +18,11 @@ package com.android.quickstep; import static android.os.Trace.TRACE_TAG_APP; import static android.view.RemoteAnimationTarget.MODE_CLOSING; 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.testing.shared.TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE; 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; @@ -344,6 +346,8 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { // Workaround for b/78520668, explicitly trim memory once UI is hidden onTrimMemory(TRIM_MEMORY_UI_HIDDEN); mFallbackRecentsView.updateLocusId(); + AccessibilityManagerCompat.sendTestProtocolEventToTest( + this, LAUNCHER_ACTIVITY_STOPPED_MESSAGE); } @Override diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 27de20cec4..94ed5b9cb0 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -17,7 +17,6 @@ package com.android.quickstep; import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; -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.StagePosition; @@ -102,6 +101,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; +import java.util.List; /** * Holds the reference to SystemUI. @@ -147,6 +147,9 @@ public class SystemUiProxy implements ISystemUiProxy { private IDesktopTaskListener mDesktopTaskListener; private final LinkedHashMap<RemoteTransition, TransitionFilter> mRemoteTransitions = new LinkedHashMap<>(); + + private final List<Runnable> mStateChangeCallbacks = new ArrayList<>(); + private IBinder mOriginalTransactionToken = null; private IOnBackInvokedCallback mBackToLauncherCallback; private IRemoteAnimationRunner mBackToLauncherRunner; @@ -268,6 +271,7 @@ public class SystemUiProxy implements ISystemUiProxy { setDesktopTaskListener(mDesktopTaskListener); setAssistantOverridesRequested( AssistUtils.newInstance(mContext).getSysUiAssistOverrideInvocationTypes()); + mStateChangeCallbacks.forEach(Runnable::run); } /** @@ -278,6 +282,20 @@ public class SystemUiProxy implements ISystemUiProxy { setProxy(null, null, null, null, null, null, null, null, null, null, null, null, null); } + /** + * Adds a callback to be notified whenever the active state changes + */ + public void addOnStateChangeListener(Runnable callback) { + mStateChangeCallbacks.add(callback); + } + + /** + * Removes a previously added state change callback + */ + public void removeOnStateChangeListener(Runnable callback) { + mStateChangeCallbacks.remove(callback); + } + // TODO(141886704): Find a way to remove this public void setLastSystemUiStateFlags(int stateFlags) { mLastSystemUiStateFlags = stateFlags; @@ -1082,6 +1100,25 @@ public class SystemUiProxy implements ISystemUiProxy { } /** + * Returns a surface which can be used to attach overlays to home task or null if + * the task doesn't exist or sysui is not connected + */ + @Nullable + public SurfaceControl getHomeTaskOverlayContainer() { + // Use a local reference as this method can be called on a worker thread, which can lead + // to NullPointer exceptions if mShellTransitions is modified on the main thread. + IShellTransitions shellTransitions = mShellTransitions; + if (shellTransitions != null) { + try { + return mShellTransitions.getHomeTaskOverlayContainer(); + } catch (RemoteException e) { + Log.w(TAG, "Failed call getOverlayContainerForTask", e); + } + } + return null; + } + + /** * Use SystemUI's transaction-queue instead of Launcher's independent one. This is necessary * if Launcher and SystemUI need to coordinate transactions (eg. for shell transitions). */ diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java index 20a751be7a..1b3f5989c4 100644 --- a/quickstep/src/com/android/quickstep/TaskIconCache.java +++ b/quickstep/src/com/android/quickstep/TaskIconCache.java @@ -31,7 +31,6 @@ import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.text.TextUtils; import android.util.SparseArray; -import android.view.accessibility.AccessibilityManager; import androidx.annotation.WorkerThread; @@ -45,6 +44,7 @@ import com.android.launcher3.pm.UserCache; 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.FlagOp; import com.android.launcher3.util.Preconditions; import com.android.quickstep.util.CancellableTask; import com.android.quickstep.util.TaskKeyLruCache; @@ -62,7 +62,6 @@ import java.util.function.Consumer; public class TaskIconCache implements DisplayInfoChangeListener { private final Executor mBgExecutor; - private final AccessibilityManager mAccessibilityManager; private final Context mContext; private final TaskKeyLruCache<TaskCacheEntry> mIconCache; @@ -79,7 +78,6 @@ public class TaskIconCache implements DisplayInfoChangeListener { public TaskIconCache(Context context, Executor bgExecutor, IconProvider iconProvider) { mContext = context; mBgExecutor = bgExecutor; - mAccessibilityManager = context.getSystemService(AccessibilityManager.class); mIconProvider = iconProvider; Resources res = context.getResources(); @@ -238,14 +236,11 @@ public class TaskIconCache implements DisplayInfoChangeListener { if ((index = mDefaultIcons.indexOfKey(userId)) >= 0) { return mDefaultIcons.valueAt(index).newIcon(mContext); } else { - try (BaseIconFactory li = getIconFactory()) { - BitmapInfo info = mDefaultIconBase.withFlags( - li.getBitmapFlagOp(new IconOptions() - .setUser(UserCache.INSTANCE.get(mContext) - .getUserInfo(UserHandle.of(userId))))); - mDefaultIcons.put(userId, info); - return info.newIcon(mContext); - } + BitmapInfo info = mDefaultIconBase.withFlags( + UserCache.INSTANCE.get(mContext).getUserInfo(UserHandle.of(userId)) + .applyBitmapInfoFlags(FlagOp.NO_OP)); + mDefaultIcons.put(userId, info); + return info.newIcon(mContext); } } } diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java index e5fca4bb24..9e2159516f 100644 --- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java +++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java @@ -195,11 +195,15 @@ public class TaskThumbnailCache { return null; } - CancellableTask<ThumbnailData> request = new CancellableTask<ThumbnailData>() { + CancellableTask<ThumbnailData> request = new CancellableTask<>() { @Override public ThumbnailData getResultOnBg() { - return ActivityManagerWrapper.getInstance().getTaskThumbnail( + ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance().getTaskThumbnail( key.id, lowResolution); + if (thumbnailData.thumbnail != null) { + return thumbnailData; + } + return ActivityManagerWrapper.getInstance().takeTaskThumbnail(key.id); } @Override diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index ddddc89a78..11c5ab476a 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -18,8 +18,6 @@ package com.android.quickstep; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; 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; @@ -421,106 +419,34 @@ public final class TaskViewUtils { * 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. - * - * 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) { - AnimatorSet animatorSet = new AnimatorSet(); - animatorSet.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - finishCallback.run(); - } - }); - - final RemoteAnimationTarget[] appTargets = - RemoteAnimationTargetCompat.wrapApps(transitionInfo, t, null /* leashMap */); - final RemoteAnimationTarget[] wallpaperTargets = - RemoteAnimationTargetCompat.wrapNonApps( - transitionInfo, true /* wallpapers */, t, null /* leashMap */); - final RemoteAnimationTarget[] nonAppTargets = - RemoteAnimationTargetCompat.wrapNonApps( - transitionInfo, false /* wallpapers */, t, null /* leashMap */); - final RecentsView recentsView = launchingTaskView.getRecentsView(); - composeRecentsLaunchAnimator(animatorSet, launchingTaskView, - appTargets, wallpaperTargets, nonAppTargets, - true, stateManager, - recentsView, depthController); - - t.apply(); - animatorSet.start(); - return; - } - - 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; - } - final int taskId = change.getTaskInfo().taskId; - final int mode = change.getMode(); - - // Find the target tasks' root tasks since those are the split stages that need to - // be animated (the tasks themselves are children and thus inherit animation). - if (taskId == initialTaskId || taskId == secondTaskId) { - if (!(mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { - throw new IllegalStateException( - "Expected task to be showing, but it is " + mode); - } - } - 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()); - } - } - - 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(); - } - + @NonNull TransitionInfo transitionInfo, SurfaceControl.Transaction t, + @NonNull Runnable finishCallback) { + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { finishCallback.run(); } }); - 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); - } + final RemoteAnimationTarget[] appTargets = + RemoteAnimationTargetCompat.wrapApps(transitionInfo, t, null /* leashMap */); + final RemoteAnimationTarget[] wallpaperTargets = + RemoteAnimationTargetCompat.wrapNonApps( + transitionInfo, true /* wallpapers */, t, null /* leashMap */); + final RemoteAnimationTarget[] nonAppTargets = + RemoteAnimationTargetCompat.wrapNonApps( + transitionInfo, false /* wallpapers */, t, null /* leashMap */); + final RecentsView recentsView = launchingTaskView.getRecentsView(); + composeRecentsLaunchAnimator(animatorSet, launchingTaskView, appTargets, wallpaperTargets, + nonAppTargets, /* launcherClosing */ true, stateManager, recentsView, + depthController); + t.apply(); - animator.start(); + animatorSet.start(); } /** diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java index 57c05e93cf..4d47f07b04 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java @@ -56,5 +56,5 @@ public class NavHandleLongPressHandler implements ResourceBasedOverride { * Called when nav handle gesture is finished by the user lifting their finger or the system * cancelling the touch for some other reason. */ - public void onTouchFinished() {} + public void onTouchFinished(String reason) {} } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java index 0127cc989f..0a558e2a98 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java @@ -81,7 +81,7 @@ public class NavHandleLongPressInputConsumer extends DelegateInputConsumer { if (mDelegate.allowInterceptByParent()) { handleMotionEvent(ev); } else if (MAIN_EXECUTOR.getHandler().hasCallbacks(mTriggerLongPress)) { - cancelLongPress(); + cancelLongPress("intercept disallowed by child input consumer"); } if (mState != STATE_ACTIVE) { @@ -113,10 +113,11 @@ public class NavHandleLongPressInputConsumer extends DelegateInputConsumer { float dy = ev.getY() - mCurrentDownEvent.getY(); double distanceSquared = (dx * dx) + (dy * dy); if (distanceSquared > touchSlopSquared) { - cancelLongPress(); + cancelLongPress("touch slop passed"); } } - case MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> cancelLongPress(); + case MotionEvent.ACTION_UP -> cancelLongPress("touch action up"); + case MotionEvent.ACTION_CANCEL -> cancelLongPress("touch action cancel"); } // If the gesture is deep press then trigger long press asap @@ -158,9 +159,9 @@ public class NavHandleLongPressInputConsumer extends DelegateInputConsumer { } } - private void cancelLongPress() { + private void cancelLongPress(String reason) { MAIN_EXECUTOR.getHandler().removeCallbacks(mTriggerLongPress); - mNavHandleLongPressHandler.onTouchFinished(); + mNavHandleLongPressHandler.onTouchFinished(reason); } private boolean isInNavBarHorizontalArea(float x) { diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java index 12a8bd9ec3..cd180ba82d 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java @@ -22,15 +22,12 @@ import static android.view.MotionEvent.INVALID_POINTER_ID; import static com.android.launcher3.Flags.enableCursorHoverStates; import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent; -import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TOUCHING; import android.content.Context; import android.content.res.Resources; 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 android.view.ViewConfiguration; @@ -39,7 +36,6 @@ import androidx.annotation.Nullable; 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; @@ -51,22 +47,13 @@ import com.android.quickstep.OverviewCommandHelper; import com.android.systemui.shared.system.InputMonitorCompat; /** - * Listens for touch and hover events to unstash the Taskbar. - * - * <p>Cancels the current gesture if the long press causes the Taskbar to be unstashed. + * Listens for touch (swipe) and hover events to unstash the Taskbar. */ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { private final TaskbarActivityContext mTaskbarActivityContext; private final OverviewCommandHelper mOverviewCommandHelper; - private final GestureDetector mLongPressDetector; - private final float mSquaredTouchSlop; - - private float mLongPressDownX, mLongPressDownY; - private boolean mCanceledUnstashHint; private final float mUnstashArea; - private final float mScreenWidth; - private final int mTaskbarNavThreshold; private final int mTaskbarNavThresholdY; private final boolean mIsTaskbarAllAppsOpen; @@ -96,10 +83,7 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { super(delegate, inputMonitor); mTaskbarActivityContext = taskbarActivityContext; mOverviewCommandHelper = overviewCommandHelper; - // TODO(b/270395798): remove this when cleaning up old Persistent Taskbar code. - mSquaredTouchSlop = Utilities.squaredTouchSlop(context); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); - mScreenWidth = taskbarActivityContext.getDeviceProfile().widthPx; Resources res = context.getResources(); mUnstashArea = res.getDimensionPixelSize(R.dimen.taskbar_unstash_input_area); @@ -111,13 +95,6 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { mIsTransientTaskbar = DisplayController.isTransientTaskbar(context); - mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() { - @Override - public void onLongPress(MotionEvent motionEvent) { - onLongPressDetected(motionEvent); - } - }); - mBottomScreenEdge = res.getDimensionPixelSize( R.dimen.taskbar_stashed_screen_edge_hover_deadzone_height); mStashedTaskbarBottomEdge = @@ -135,7 +112,6 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { @Override public void onMotionEvent(MotionEvent ev) { - mLongPressDetector.onTouchEvent(ev); if (mState != STATE_ACTIVE) { boolean isStashedTaskbarHovered = isMouseEvent(ev) && isStashedTaskbarHovered((int) ev.getX(), (int) ev.getY()); @@ -152,15 +128,6 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { mHasPassedTaskbarNavThreshold = false; mTaskbarActivityContext.setAutohideSuspendFlag( FLAG_AUTOHIDE_SUSPEND_TOUCHING, true); - if (isInTaskbarArea(x)) { - if (!mIsTransientTaskbar) { - mLongPressDownX = x; - mLongPressDownY = y; - mTaskbarActivityContext.startTaskbarUnstashHint( - /* animateForward = */ true); - mCanceledUnstashHint = false; - } - } if (mTransitionCallback != null && !mIsTaskbarAllAppsOpen) { mTransitionCallback.onActionDown(); } @@ -181,15 +148,6 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { } break; case MotionEvent.ACTION_MOVE: - if (!mIsTransientTaskbar - && !mCanceledUnstashHint - && squaredHypot(mLongPressDownX - x, mLongPressDownY - y) - > mSquaredTouchSlop) { - mTaskbarActivityContext.startTaskbarUnstashHint( - /* animateForward = */ false); - mCanceledUnstashHint = true; - } - int pointerIndex = ev.findPointerIndex(mActivePointerId); if (pointerIndex == INVALID_POINTER_ID) { break; @@ -283,10 +241,6 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { } private void cleanupAfterMotionEvent() { - if (!mIsTransientTaskbar && !mCanceledUnstashHint) { - mTaskbarActivityContext.startTaskbarUnstashHint( - /* animateForward = */ false); - } mTaskbarActivityContext.setAutohideSuspendFlag( FLAG_AUTOHIDE_SUSPEND_TOUCHING, false); if (mTransitionCallback != null) { @@ -298,12 +252,6 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { mIsPassedBubbleBarSlop = false; } - 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) { return false; @@ -320,17 +268,6 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { } } - private void onLongPressDetected(MotionEvent motionEvent) { - if (mTaskbarActivityContext != null - && isInTaskbarArea(motionEvent.getRawX()) - && !mIsTransientTaskbar) { - boolean taskBarPressed = mTaskbarActivityContext.onLongPressToUnstashTaskbar(); - if (taskBarPressed) { - setActive(motionEvent); - } - } - } - /** * Listen for hover events for the stashed taskbar. * @@ -389,7 +326,7 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { } private void startStashedTaskbarHover(boolean isHovered) { - mTaskbarActivityContext.startTaskbarUnstashHint(isHovered, /* forceUnstash = */ true); + mTaskbarActivityContext.startTaskbarUnstashHint(isHovered); mIsStashedTaskbarHovered = isHovered; } diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java index 69c15a5973..c91ee81067 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java @@ -25,6 +25,7 @@ import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_USE_T import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Insets; @@ -86,6 +87,8 @@ abstract class TutorialFragment extends GestureSandboxFragment implements OnTouc private boolean mIsFoldable; private boolean mOnAttachedToWindowPendingCreate; + @Nullable private Runnable mOnAttachedOnGlobalLayoutCallback = null; + public static TutorialFragment newInstance( TutorialType tutorialType, boolean gestureComplete, boolean fromTutorialMenu) { TutorialFragment fragment = getFragmentForTutorialType(tutorialType, fromTutorialMenu); @@ -349,13 +352,27 @@ abstract class TutorialFragment extends GestureSandboxFragment implements OnTouc new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { - changeController(mTutorialType); + runOnAttached(() -> changeController(mTutorialType)); mRootView.getViewTreeObserver().removeOnGlobalLayoutListener(this); } }); } } + private void runOnAttached(Runnable callback) { + mOnAttachedOnGlobalLayoutCallback = callback; + if (getContext() != null) { + onAttached(); + } + } + + private void onAttached() { + if (mOnAttachedOnGlobalLayoutCallback != null) { + mOnAttachedOnGlobalLayoutCallback.run(); + mOnAttachedOnGlobalLayoutCallback = null; + } + } + @Override public boolean onTouch(View view, MotionEvent motionEvent) { if (mTutorialController != null && !isGestureComplete()) { @@ -378,6 +395,12 @@ abstract class TutorialFragment extends GestureSandboxFragment implements OnTouc } @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + onAttached(); + } + + @Override void onAttachedToWindow() { if (mEdgeBackGestureHandler == null) { mOnAttachedToWindowPendingCreate = true; diff --git a/quickstep/src/com/android/quickstep/util/AnimUtils.java b/quickstep/src/com/android/quickstep/util/AnimUtils.java index b7b7825d78..7fbbb6e5b8 100644 --- a/quickstep/src/com/android/quickstep/util/AnimUtils.java +++ b/quickstep/src/com/android/quickstep/util/AnimUtils.java @@ -39,4 +39,13 @@ public class AnimUtils { ? SplitAnimationTimings.TABLET_SPLIT_TO_CONFIRM : SplitAnimationTimings.PHONE_SPLIT_TO_CONFIRM; } + + /** + * Fetches device-specific timings for the app pair launch animation. + */ + public static SplitAnimationTimings getDeviceAppPairLaunchTimings(boolean isTablet) { + return isTablet + ? SplitAnimationTimings.TABLET_APP_PAIR_LAUNCH + : SplitAnimationTimings.PHONE_APP_PAIR_LAUNCH; + } } diff --git a/quickstep/src/com/android/quickstep/util/AppPairLaunchTimings.kt b/quickstep/src/com/android/quickstep/util/AppPairLaunchTimings.kt new file mode 100644 index 0000000000..086c8af6b4 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/AppPairLaunchTimings.kt @@ -0,0 +1,72 @@ +/* + * 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 com.android.app.animation.Interpolators + +/** Timings for the app pair launch animation. */ +abstract class AppPairLaunchTimings : SplitAnimationTimings { + protected abstract val STAGED_RECT_SLIDE_DURATION: Int + + // Common timings that apply to app pair launches on any type of device + override fun getStagedRectSlideStart() = 0 + override fun getStagedRectSlideEnd() = stagedRectSlideStart + STAGED_RECT_SLIDE_DURATION + override fun getPlaceholderFadeInStart() = 0 + override fun getPlaceholderFadeInEnd() = 0 + override fun getPlaceholderIconFadeInStart() = 0 + override fun getPlaceholderIconFadeInEnd() = 0 + + private val iconFadeStart: Int + get() = getStagedRectSlideEnd() + private val iconFadeEnd: Int + get() = iconFadeStart + 83 + private val appRevealStart: Int + get() = getStagedRectSlideEnd() + 67 + private val appRevealEnd: Int + get() = appRevealStart + 217 + private val cellSplitStart: Int + get() = (getStagedRectSlideEnd() * 0.83f).toInt() + private val cellSplitEnd: Int + get() = cellSplitStart + 500 + + override fun getStagedRectXInterpolator() = Interpolators.EMPHASIZED_COMPLEMENT + override fun getStagedRectYInterpolator() = Interpolators.EMPHASIZED + override fun getStagedRectScaleXInterpolator() = Interpolators.EMPHASIZED + override fun getStagedRectScaleYInterpolator() = Interpolators.EMPHASIZED + override fun getCellSplitInterpolator() = Interpolators.EMPHASIZED + override fun getIconFadeInterpolator() = Interpolators.LINEAR + + override fun getCellSplitStartOffset(): Float { + return cellSplitStart.toFloat() / getDuration() + } + override fun getCellSplitEndOffset(): Float { + return cellSplitEnd.toFloat() / getDuration() + } + override fun getIconFadeStartOffset(): Float { + return iconFadeStart.toFloat() / getDuration() + } + override fun getIconFadeEndOffset(): Float { + return iconFadeEnd.toFloat() / getDuration() + } + override fun getAppRevealStartOffset(): Float { + return appRevealStart.toFloat() / getDuration() + } + override fun getAppRevealEndOffset(): Float { + return appRevealEnd.toFloat() / getDuration() + } + abstract override fun getDuration(): Int +} diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java index cc3b54b0ca..3ca2531cd4 100644 --- a/quickstep/src/com/android/quickstep/util/AppPairsController.java +++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java @@ -36,6 +36,7 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; +import com.android.launcher3.apppairs.AppPairIcon; import com.android.launcher3.icons.IconCache; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.data.FolderInfo; @@ -120,11 +121,14 @@ public class AppPairsController { * 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) { + public void launchAppPair(AppPairIcon appPairIcon) { + WorkspaceItemInfo app1 = appPairIcon.getInfo().contents.get(0); + WorkspaceItemInfo app2 = appPairIcon.getInfo().contents.get(1); ComponentKey app1Key = new ComponentKey(app1.getTargetComponent(), app1.user); ComponentKey app2Key = new ComponentKey(app2.getTargetComponent(), app2.user); mSplitSelectStateController.findLastActiveTasksAndRunCallback( Arrays.asList(app1Key, app2Key), + false /* findExactPairMatch */, foundTasks -> { @Nullable Task foundTask1 = foundTasks.get(0); Intent task1Intent; @@ -151,9 +155,12 @@ public class AppPairsController { app2.intent, app2.user); } + mSplitSelectStateController.setLaunchingIconView(appPairIcon); + mSplitSelectStateController.launchSplitTasks( AppPairsController.convertRankToSnapPosition(app1.rank)); - }); + } + ); } /** diff --git a/quickstep/src/com/android/quickstep/util/AssistStateManager.java b/quickstep/src/com/android/quickstep/util/AssistStateManager.java index a0bb76ded4..f7437eb075 100644 --- a/quickstep/src/com/android/quickstep/util/AssistStateManager.java +++ b/quickstep/src/com/android/quickstep/util/AssistStateManager.java @@ -31,6 +31,11 @@ public class AssistStateManager implements ResourceBasedOverride { public AssistStateManager() {} + /** Whether search supports haptic on invocation. */ + public boolean supportsCommitHaptic() { + return false; + } + /** Whether search is available. */ public boolean isSearchAvailable() { return false; diff --git a/quickstep/src/com/android/quickstep/util/PhoneAppPairLaunchTimings.kt b/quickstep/src/com/android/quickstep/util/PhoneAppPairLaunchTimings.kt new file mode 100644 index 0000000000..beab90f15f --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/PhoneAppPairLaunchTimings.kt @@ -0,0 +1,23 @@ +/* + * 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 + +/** Timings for the app pair launch animation on phones. */ +class PhoneAppPairLaunchTimings : AppPairLaunchTimings(), SplitAnimationTimings { + override val STAGED_RECT_SLIDE_DURATION = 500 + override fun getDuration() = SplitAnimationTimings.PHONE_APP_PAIR_LAUNCH_DURATION +} diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt index dfbd32c8f3..ade8074d8b 100644 --- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt +++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt @@ -21,20 +21,37 @@ import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.AnimatorSet import android.animation.ObjectAnimator +import android.animation.ValueAnimator +import android.app.ActivityManager.RunningTaskInfo import android.graphics.Bitmap import android.graphics.Rect import android.graphics.RectF import android.graphics.drawable.Drawable +import android.view.RemoteAnimationTarget +import android.view.SurfaceControl +import android.view.SurfaceControl.Transaction import android.view.View +import android.view.WindowManager +import android.window.TransitionInfo +import android.window.TransitionInfo.Change +import androidx.annotation.VisibleForTesting import com.android.app.animation.Interpolators import com.android.launcher3.DeviceProfile +import com.android.launcher3.Launcher +import com.android.launcher3.QuickstepTransitionManager import com.android.launcher3.Utilities import com.android.launcher3.anim.PendingAnimation +import com.android.launcher3.apppairs.AppPairIcon import com.android.launcher3.config.FeatureFlags +import com.android.launcher3.statehandlers.DepthController +import com.android.launcher3.statemanager.StateManager import com.android.launcher3.statemanager.StatefulActivity import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource import com.android.launcher3.views.BaseDragLayer +import com.android.quickstep.TaskViewUtils +import com.android.quickstep.views.FloatingAppPairView import com.android.quickstep.views.FloatingTaskView +import com.android.quickstep.views.GroupedTaskView import com.android.quickstep.views.RecentsView import com.android.quickstep.views.SplitInstructionsView import com.android.quickstep.views.TaskThumbnailView @@ -308,6 +325,407 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC pendingAnimation.buildAnim().start() } + /** + * Called when launching a specific pair of apps, e.g. when tapping a pair of apps in Overview, + * or launching an app pair from its Home icon. Selects the appropriate launch animation and + * plays it. + */ + fun playSplitLaunchAnimation( + launchingTaskView: GroupedTaskView?, + launchingIconView: AppPairIcon?, + initialTaskId: Int, + secondTaskId: Int, + apps: Array<RemoteAnimationTarget>?, + wallpapers: Array<RemoteAnimationTarget>?, + nonApps: Array<RemoteAnimationTarget>?, + stateManager: StateManager<*>, + depthController: DepthController?, + info: TransitionInfo?, + t: Transaction?, + finishCallback: Runnable + ) { + if (info == null && t == null) { + // (Legacy animation) Tapping a split tile in Overview + // TODO (b/315490678): Ensure that this works with app pairs flow + check(apps != null && wallpapers != null && nonApps != null) { + "trying to call composeRecentsSplitLaunchAnimatorLegacy, but encountered an " + + "unexpected null" + } + + composeRecentsSplitLaunchAnimatorLegacy( + launchingTaskView, + initialTaskId, + secondTaskId, + apps, + wallpapers, + nonApps, + stateManager, + depthController, + finishCallback + ) + + return + } + + if (launchingTaskView != null) { + // Tapping a split tile in Overview + check(info != null && t != null) { + "trying to launch a GroupedTaskView, but encountered an unexpected null" + } + + composeRecentsSplitLaunchAnimator( + launchingTaskView, + stateManager, + depthController, + info, + t, + finishCallback + ) + } else if (launchingIconView != null) { + // Tapping an app pair icon + check(info != null && t != null) { + "trying to launch an app pair icon, but encountered an unexpected null" + } + + composeIconSplitLaunchAnimator( + launchingIconView, + initialTaskId, + secondTaskId, + info, + t, + finishCallback + ) + } else { + // Fallback case: simple fade-in animation + check(info != null && t != null) { + "trying to call composeFadeInSplitLaunchAnimator, but encountered an " + + "unexpected null" + } + + composeFadeInSplitLaunchAnimator(initialTaskId, secondTaskId, info, t, finishCallback) + } + } + + /** + * When the user taps a split tile in Overview, this will play the tasks' launch animation from + * the position of the tapped tile. + */ + @VisibleForTesting + fun composeRecentsSplitLaunchAnimator( + launchingTaskView: GroupedTaskView, + stateManager: StateManager<*>, + depthController: DepthController?, + info: TransitionInfo, + t: Transaction, + finishCallback: Runnable + ) { + TaskViewUtils.composeRecentsSplitLaunchAnimator( + launchingTaskView, + stateManager, + depthController, + info, + t, + finishCallback + ) + } + + /** + * LEGACY VERSION: When the user taps a split tile in Overview, this will play the tasks' launch + * animation from the position of the tapped tile. + */ + @VisibleForTesting + fun composeRecentsSplitLaunchAnimatorLegacy( + launchingTaskView: GroupedTaskView?, + initialTaskId: Int, + secondTaskId: Int, + apps: Array<RemoteAnimationTarget>, + wallpapers: Array<RemoteAnimationTarget>, + nonApps: Array<RemoteAnimationTarget>, + stateManager: StateManager<*>, + depthController: DepthController?, + finishCallback: Runnable + ) { + TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy( + launchingTaskView, + initialTaskId, + secondTaskId, + apps, + wallpapers, + nonApps, + stateManager, + depthController, + finishCallback + ) + } + + /** + * When the user taps an app pair icon to launch split, this will play the tasks' launch + * animation from the position of the icon. + */ + @VisibleForTesting + fun composeIconSplitLaunchAnimator( + launchingIconView: AppPairIcon, + initialTaskId: Int, + secondTaskId: Int, + transitionInfo: TransitionInfo, + t: Transaction, + finishCallback: Runnable + ) { + val launcher = Launcher.getLauncher(launchingIconView.context) + val dp = launcher.deviceProfile + + // Create an AnimatorSet that will run both shell and launcher transitions together + val launchAnimation = AnimatorSet() + val progressUpdater = ValueAnimator.ofFloat(0f, 1f) + val timings = AnimUtils.getDeviceAppPairLaunchTimings(dp.isTablet) + progressUpdater.setDuration(timings.getDuration().toLong()) + progressUpdater.interpolator = Interpolators.LINEAR + + // Find the root shell leash that we want to fade in (parent of both app windows and + // the divider). For simplicity, we search using the initialTaskId. + var rootShellLayer: SurfaceControl? = null + var dividerPos = 0 + + for (change in transitionInfo.changes) { + val taskInfo: RunningTaskInfo = change.taskInfo ?: continue + val taskId = taskInfo.taskId + val mode = change.mode + + if (taskId == initialTaskId || taskId == secondTaskId) { + check( + mode == WindowManager.TRANSIT_OPEN || mode == WindowManager.TRANSIT_TO_FRONT + ) { + "Expected task to be showing, but it is $mode" + } + } + + if (taskId == initialTaskId) { + var splitRoot1 = change + val parentToken = change.parent + if (parentToken != null) { + splitRoot1 = transitionInfo.getChange(parentToken) ?: change + } + + val topLevelToken = splitRoot1.parent + if (topLevelToken != null) { + rootShellLayer = transitionInfo.getChange(topLevelToken)?.leash + } + + dividerPos = + if (dp.isLeftRightSplit) change.endAbsBounds.right + else change.endAbsBounds.bottom + } + } + + check(rootShellLayer != null) { + "Could not find a TransitionInfo.Change matching the initialTaskId" + } + + // Shell animation: the apps are revealed toward end of the launch animation + progressUpdater.addUpdateListener { valueAnimator: ValueAnimator -> + val progress = + Interpolators.clampToProgress( + Interpolators.LINEAR, + valueAnimator.animatedFraction, + timings.appRevealStartOffset, + timings.appRevealEndOffset + ) + + // Set the alpha of the shell layer (2 apps + divider) + t.setAlpha(rootShellLayer, progress) + t.apply() + } + + // Create a new floating view in Launcher, positioned above the launching icon + val drawableArea = launchingIconView.iconDrawableArea + val appIcon1 = launchingIconView.info.contents[0].newIcon(launchingIconView.context) + val appIcon2 = launchingIconView.info.contents[1].newIcon(launchingIconView.context) + appIcon1.setBounds(0, 0, dp.iconSizePx, dp.iconSizePx) + appIcon2.setBounds(0, 0, dp.iconSizePx, dp.iconSizePx) + val floatingView = + FloatingAppPairView.getFloatingAppPairView( + launcher, + drawableArea, + appIcon1, + appIcon2, + dividerPos + ) + + // Launcher animation: animate the floating view, expanding to fill the display surface + progressUpdater.addUpdateListener( + object : MultiValueUpdateListener() { + var mDx = + FloatProp( + floatingView.startingPosition.left, + dp.widthPx / 2f - floatingView.startingPosition.width() / 2f, + 0f /* delay */, + timings.getDuration().toFloat(), + Interpolators.clampToProgress( + timings.getStagedRectXInterpolator(), + timings.stagedRectSlideStartOffset, + timings.stagedRectSlideEndOffset + ) + ) + var mDy = + FloatProp( + floatingView.startingPosition.top, + dp.heightPx / 2f - floatingView.startingPosition.height() / 2f, + 0f /* delay */, + timings.getDuration().toFloat(), + Interpolators.clampToProgress( + Interpolators.EMPHASIZED, + timings.stagedRectSlideStartOffset, + timings.stagedRectSlideEndOffset + ) + ) + var mScaleX = + FloatProp( + 1f /* start */, + dp.widthPx / floatingView.startingPosition.width(), + 0f /* delay */, + timings.getDuration().toFloat(), + Interpolators.clampToProgress( + Interpolators.EMPHASIZED, + timings.stagedRectSlideStartOffset, + timings.stagedRectSlideEndOffset + ) + ) + var mScaleY = + FloatProp( + 1f /* start */, + dp.heightPx / floatingView.startingPosition.height(), + 0f /* delay */, + timings.getDuration().toFloat(), + Interpolators.clampToProgress( + Interpolators.EMPHASIZED, + timings.stagedRectSlideStartOffset, + timings.stagedRectSlideEndOffset + ) + ) + + override fun onUpdate(percent: Float, initOnly: Boolean) { + floatingView.progress = percent + floatingView.x = mDx.value + floatingView.y = mDy.value + floatingView.scaleX = mScaleX.value + floatingView.scaleY = mScaleY.value + floatingView.invalidate() + } + } + ) + + // When animation ends, remove the floating view and run finishCallback + progressUpdater.addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + safeRemoveViewFromDragLayer(launcher, floatingView) + finishCallback.run() + } + } + ) + + launchAnimation.play(progressUpdater) + launchAnimation.start() + } + + /** + * If we are launching split screen without any special animation from a starting View, we + * simply fade in the starting apps and fade out launcher. + */ + @VisibleForTesting + fun composeFadeInSplitLaunchAnimator( + initialTaskId: Int, + secondTaskId: Int, + transitionInfo: TransitionInfo, + t: Transaction, + finishCallback: Runnable + ) { + var splitRoot1: Change? = null + var splitRoot2: Change? = null + val openingTargets = ArrayList<SurfaceControl>() + for (change in transitionInfo.changes) { + val taskInfo: RunningTaskInfo = change.taskInfo ?: continue + val taskId = taskInfo.taskId + val mode = change.mode + + // Find the target tasks' root tasks since those are the split stages that need to + // be animated (the tasks themselves are children and thus inherit animation). + if (taskId == initialTaskId || taskId == secondTaskId) { + check( + mode == WindowManager.TRANSIT_OPEN || mode == WindowManager.TRANSIT_TO_FRONT + ) { + "Expected task to be showing, but it is $mode" + } + } + + if (taskId == initialTaskId) { + splitRoot1 = change + val parentToken1 = change.parent + if (parentToken1 != null) { + splitRoot1 = transitionInfo.getChange(parentToken1) ?: change + } + + if (splitRoot1?.leash != null) { + openingTargets.add(splitRoot1.leash) + } + } + + if (taskId == secondTaskId) { + splitRoot2 = change + val parentToken2 = change.parent + if (parentToken2 != null) { + splitRoot2 = transitionInfo.getChange(parentToken2) ?: change + } + + if (splitRoot2?.leash != null) { + openingTargets.add(splitRoot2.leash) + } + } + } + + val animTransaction = Transaction() + val animator = ValueAnimator.ofFloat(0f, 1f) + animator.setDuration(QuickstepTransitionManager.SPLIT_LAUNCH_DURATION.toLong()) + animator.addUpdateListener { valueAnimator: ValueAnimator -> + val progress = valueAnimator.animatedFraction + for (leash in openingTargets) { + animTransaction.setAlpha(leash, progress) + } + animTransaction.apply() + } + + animator.addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationStart(animation: Animator) { + for (leash in openingTargets) { + animTransaction.show(leash).setAlpha(leash, 0.0f) + } + animTransaction.apply() + } + + override fun onAnimationEnd(animation: Animator) { + finishCallback.run() + } + } + ) + + if (splitRoot1 != null) { + // Set the highest level split root alpha; we could technically use the parent of + // either splitRoot1 or splitRoot2 + val parentToken = splitRoot1.parent + var rootLayer: Change? = null + if (parentToken != null) { + rootLayer = transitionInfo.getChange(parentToken) + } + if (rootLayer != null && rootLayer.leash != null) { + t.setAlpha(rootLayer.leash, 1f) + } + } + + t.apply() + animator.start() + } + 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 93f2255a4d..b618546576 100644 --- a/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java +++ b/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java @@ -21,31 +21,44 @@ import static com.android.app.animation.Interpolators.LINEAR; import android.view.animation.Interpolator; /** - * An interface that supports the centralization of timing information for splitscreen animations. + * Organizes timing information for split screen animations. */ public interface SplitAnimationTimings { + /** Total duration (ms) for initiating split screen (staging the first app) on tablets. */ int TABLET_ENTER_DURATION = 866; + /** Total duration (ms) for confirming split screen (selecting the second app) on tablets. */ int TABLET_CONFIRM_DURATION = 500; - + /** Total duration (ms) for initiating split screen (staging the first app) on phones. */ int PHONE_ENTER_DURATION = 517; + /** Total duration (ms) for confirming split screen (selecting the second app) on phones. */ int PHONE_CONFIRM_DURATION = 333; - + /** Total duration (ms) for aborting split screen (before selecting the second app). */ int ABORT_DURATION = 500; + /** Total duration (ms) for launching an app pair from its icon on tablets. */ + int TABLET_APP_PAIR_LAUNCH_DURATION = 998; + /** Total duration (ms) for launching an app pair from its icon on phones. */ + int PHONE_APP_PAIR_LAUNCH_DURATION = 915; + // Initialize timing classes so they can be accessed statically SplitAnimationTimings TABLET_OVERVIEW_TO_SPLIT = new TabletOverviewToSplitTimings(); SplitAnimationTimings TABLET_HOME_TO_SPLIT = new TabletHomeToSplitTimings(); SplitAnimationTimings TABLET_SPLIT_TO_CONFIRM = new TabletSplitToConfirmTimings(); - SplitAnimationTimings PHONE_OVERVIEW_TO_SPLIT = new PhoneOverviewToSplitTimings(); SplitAnimationTimings PHONE_SPLIT_TO_CONFIRM = new PhoneSplitToConfirmTimings(); + SplitAnimationTimings TABLET_APP_PAIR_LAUNCH = new TabletAppPairLaunchTimings(); + SplitAnimationTimings PHONE_APP_PAIR_LAUNCH = new PhoneAppPairLaunchTimings(); - // Shared methods + // Shared methods: all split animations have these parameters int getDuration(); + /** Start fading in the floating view tile at this time (in ms). */ int getPlaceholderFadeInStart(); int getPlaceholderFadeInEnd(); + /** Start fading in the app icon at this time (in ms). */ int getPlaceholderIconFadeInStart(); int getPlaceholderIconFadeInEnd(); + /** Start translating the floating view tile at this time (in ms). */ int getStagedRectSlideStart(); + /** The floating tile has reached its final position at this time (in ms). */ int getStagedRectSlideEnd(); Interpolator getStagedRectXInterpolator(); Interpolator getStagedRectYInterpolator(); @@ -70,6 +83,11 @@ public interface SplitAnimationTimings { return (float) getStagedRectSlideEnd() / getDuration(); } + // DEFAULT VALUES: We define default values here so that SplitAnimationTimings can be used + // flexibly in animation-running functions, e.g. a single function that handles 2 types of split + // animations. The values are not intended to be used, and can safely be removed if refactoring + // these classes. + // Defaults for OverviewToSplit default float getGridSlideStartOffset() { return 0; } default float getGridSlideStaggerOffset() { return 0; } @@ -94,5 +112,13 @@ public interface SplitAnimationTimings { // Defaults for SplitToConfirm default float getInstructionsFadeStartOffset() { return 0; } default float getInstructionsFadeEndOffset() { return 0; } + + // Defaults for AppPair + default float getCellSplitStartOffset() { return 0; } + default float getCellSplitEndOffset() { return 0; } + default float getAppRevealStartOffset() { return 0; } + default float getAppRevealEndOffset() { return 0; } + default Interpolator getCellSplitInterpolator() { return LINEAR; } + default Interpolator getIconFadeInterpolator() { return LINEAR; } } diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt b/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt index 596bb47861..38bbe601b6 100644 --- a/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt +++ b/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt @@ -56,4 +56,4 @@ class SplitScreenUtils { } } } -}
\ No newline at end of file +} diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt index 423ba43037..c013483bab 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt +++ b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt @@ -93,6 +93,7 @@ class SplitSelectDataHolder( private var secondTaskId: Int = INVALID_TASK_ID private var initialIntent: Intent? = null private var secondIntent: Intent? = null + private var widgetSecondIntent: Intent? = null private var initialUser: UserHandle? = null private var secondUser: UserHandle? = null private var initialPendingIntent: PendingIntent? = null @@ -167,6 +168,16 @@ class SplitSelectDataHolder( secondUser = pendingIntent.creatorUserHandle } + /** + * Similar to [setSecondTask] except this is to be called for widgets which can pass through + * an extra intent from their RemoteResponse. + * See [android.widget.RemoteViews.RemoteResponse.getLaunchOptions].first + */ + fun setSecondWidget(pendingIntent: PendingIntent, widgetIntent: Intent?) { + setSecondTask(pendingIntent) + widgetSecondIntent = widgetIntent + } + private fun getShortcutInfo(intent: Intent?, user: UserHandle?): ShortcutInfo? { val intentPackage = intent?.getPackage() ?: return null val shortcutId = intent.getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID) @@ -241,6 +252,7 @@ class SplitSelectDataHolder( secondTaskId, initialPendingIntent, secondPendingIntent, + widgetSecondIntent, initialUser?.identifier ?: -1, secondUser?.identifier ?: -1, initialShortcut, @@ -257,7 +269,8 @@ class SplitSelectDataHolder( * Note that both [initialIntent] and [secondIntent] will be nullified on method return * * One caveat is that if [secondPendingIntent] is set, we will use that and *not* attempt to - * convert [secondIntent] + * convert [secondIntent]. + * This also leaves [widgetSecondIntent] untouched. */ private fun convertIntentsToFinalTypes() { initialShortcut = getShortcutInfo(initialIntent, initialUser) @@ -343,6 +356,7 @@ class SplitSelectDataHolder( var secondTaskId: Int = INVALID_TASK_ID, var initialPendingIntent: PendingIntent? = null, var secondPendingIntent: PendingIntent? = null, + var widgetSecondIntent: Intent? = null, var initialUserId: Int = -1, var secondUserId: Int = -1, var initialShortcut: ShortcutInfo? = null, diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java index 145707bbfb..d5899e47f4 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -32,6 +32,7 @@ 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 static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported; +import static com.android.wm.shell.common.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT; import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50; import android.animation.Animator; @@ -70,6 +71,7 @@ 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.apppairs.AppPairIcon; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.IconProvider; import com.android.launcher3.logging.StatsLogManager; @@ -91,7 +93,6 @@ 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.RecentsView; @@ -140,6 +141,8 @@ public class SplitSelectStateController { /** If not null, this is the TaskView we want to launch from */ @Nullable private GroupedTaskView mLaunchingTaskView; + /** If not null, this is the icon we want to launch from */ + private AppPairIcon mLaunchingIconView; /** True when the first selected split app is being launched in fullscreen. */ private boolean mLaunchingFirstAppFullscreen; @@ -213,15 +216,16 @@ public class SplitSelectStateController { } /** - * 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. + * Given a list of task keys, searches through active Tasks in RecentsModel to find the last + * active instances of these tasks. Returns an empty array if there is no such running task. + * + * @param componentKeys The list of ComponentKeys to search for. + * @param callback The callback that will be executed on the list of found tasks. + * @param findExactPairMatch If {@code true}, only finds tasks that contain BOTH of the wanted + * tasks (i.e. searching for a running pair of tasks.) */ - public void findLastActiveTasksAndRunCallback( - @Nullable List<ComponentKey> componentKeys, Consumer<List<Task>> callback) { + public void findLastActiveTasksAndRunCallback(@Nullable List<ComponentKey> componentKeys, + boolean findExactPairMatch, Consumer<List<Task>> callback) { mRecentTasksModel.getTasks(taskGroups -> { if (componentKeys == null || componentKeys.isEmpty()) { callback.accept(Collections.emptyList()); @@ -229,27 +233,40 @@ public class SplitSelectStateController { } 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; + + if (findExactPairMatch) { // 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; + if (isInstanceOfAppPair( + groupTask, componentKeys.get(0), componentKeys.get(1))) { + lastActiveTasks.add(groupTask.task1); break; } } + } else { + // For each key we are looking for, add to lastActiveTasks with the corresponding + // Task (or do nothing if not found). + for (ComponentKey key : componentKeys) { + Task lastActiveTask = null; + // Loop through tasks in reverse, since they are ordered with 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); + lastActiveTasks.add(lastActiveTask); + } } callback.accept(lastActiveTasks); @@ -271,6 +288,19 @@ public class SplitSelectStateController { } /** + * Checks if a given GroupTask is a pair of apps that matches two given ComponentKeys. We check + * both permutations because task order is not guaranteed in GroupTasks. + */ + public boolean isInstanceOfAppPair(GroupTask groupTask, @NonNull ComponentKey componentKey1, + @NonNull ComponentKey componentKey2) { + return ((isInstanceOfComponent(groupTask.task1, componentKey1) + && isInstanceOfComponent(groupTask.task2, componentKey2)) + || + (isInstanceOfComponent(groupTask.task1, componentKey2) + && isInstanceOfComponent(groupTask.task2, componentKey1))); + } + + /** * Listener will only get callbacks going forward from the point of registration. No * methods will be fired upon registering. */ @@ -355,6 +385,10 @@ public class SplitSelectStateController { mSplitSelectDataHolder.setSecondTask(pendingIntent); } + public void setSecondWidget(PendingIntent pendingIntent, Intent widgetIntent) { + mSplitSelectDataHolder.setSecondWidget(pendingIntent, widgetIntent); + } + /** * 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 @@ -380,11 +414,13 @@ public class SplitSelectStateController { ShortcutInfo secondShortcut = launchData.getSecondShortcut(); PendingIntent firstPI = launchData.getInitialPendingIntent(); PendingIntent secondPI = launchData.getSecondPendingIntent(); + Intent widgetIntent = launchData.getWidgetSecondIntent(); int firstUserId = launchData.getInitialUserId(); int secondUserId = launchData.getSecondUserId(); int initialStagePosition = launchData.getInitialStagePosition(); Bundle optionsBundle = options1.toBundle(); - + Bundle extrasBundle = new Bundle(1); + extrasBundle.putParcelable(KEY_EXTRA_WIDGET_INTENT, widgetIntent); if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) { final RemoteTransition remoteTransition = getShellRemoteTransition(firstTaskId, secondTaskId, callback, "LaunchSplitPair"); @@ -396,7 +432,7 @@ public class SplitSelectStateController { case SPLIT_TASK_PENDINGINTENT -> mSystemUiProxy.startIntentAndTask(secondPI, secondUserId, optionsBundle, - firstTaskId, null /*options2*/, initialStagePosition, snapPosition, + firstTaskId, extrasBundle, initialStagePosition, snapPosition, remoteTransition, shellInstanceId); case SPLIT_TASK_SHORTCUT -> @@ -411,9 +447,9 @@ public class SplitSelectStateController { case SPLIT_PENDINGINTENT_PENDINGINTENT -> mSystemUiProxy.startIntents(firstPI, firstUserId, firstShortcut, - optionsBundle, secondPI, secondUserId, secondShortcut, - null /*options2*/, initialStagePosition, snapPosition, - remoteTransition, shellInstanceId); + optionsBundle, secondPI, secondUserId, secondShortcut, extrasBundle, + initialStagePosition, snapPosition, remoteTransition, + shellInstanceId); case SPLIT_SHORTCUT_TASK -> mSystemUiProxy.startShortcutAndTask(firstShortcut, optionsBundle, @@ -627,8 +663,20 @@ public class SplitSelectStateController { }; MAIN_EXECUTOR.execute(() -> { - TaskViewUtils.composeRecentsSplitLaunchAnimator(mLaunchingTaskView, mStateManager, - mDepthController, mInitialTaskId, mSecondTaskId, info, t, () -> { + // Only animate from taskView if it's already visible + boolean shouldLaunchFromTaskView = mLaunchingTaskView != null && + mLaunchingTaskView.getRecentsView().isTaskViewVisible(mLaunchingTaskView); + mSplitAnimationController.playSplitLaunchAnimation( + shouldLaunchFromTaskView ? mLaunchingTaskView : null, + mLaunchingIconView, + mInitialTaskId, + mSecondTaskId, + null /* apps */, + null /* wallpapers */, + null /* nonApps */, + mStateManager, + mDepthController, + info, t, () -> { finishAdapter.run(); cleanup(true /*success*/); }); @@ -684,9 +732,10 @@ public class SplitSelectStateController { RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, Runnable finishedCallback) { postAsyncCallback(mHandler, - () -> TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy( - mLaunchingTaskView, mInitialTaskId, mSecondTaskId, apps, wallpapers, - nonApps, mStateManager, mDepthController, () -> { + () -> mSplitAnimationController.playSplitLaunchAnimation(mLaunchingTaskView, + mLaunchingIconView, mInitialTaskId, mSecondTaskId, apps, wallpapers, + nonApps, mStateManager, mDepthController, null /* info */, null /* t */, + () -> { finishedCallback.run(); if (mSuccessCallback != null) { mSuccessCallback.accept(true); @@ -719,6 +768,7 @@ public class SplitSelectStateController { dispatchOnSplitSelectionExit(); mRecentsAnimationRunning = false; mLaunchingTaskView = null; + mLaunchingIconView = null; mAnimateCurrentTaskDismissal = false; mDismissingFromSplitPair = false; mFirstFloatingTaskView = null; @@ -779,6 +829,10 @@ public class SplitSelectStateController { return mAppPairsController; } + public void setLaunchingIconView(AppPairIcon launchingIconView) { + mLaunchingIconView = launchingIconView; + } + public BackPressHandler getSplitBackHandler() { return mSplitBackHandler; } diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java index 931334209b..e705285fa2 100644 --- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java +++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java @@ -72,7 +72,8 @@ public class SplitToWorkspaceController { * @return {@code true} if we can attempt launch the widget into split, {@code false} otherwise * to allow launcher to handle the click */ - public boolean handleSecondWidgetSelectionForSplit(View view, PendingIntent pendingIntent) { + public boolean handleSecondWidgetSelectionForSplit(View view, PendingIntent pendingIntent, + Intent remoteResponseIntent) { if (shouldIgnoreSecondSplitLaunch()) { return false; } @@ -86,7 +87,7 @@ public class SplitToWorkspaceController { Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); view.post(() -> { - mController.setSecondTask(pendingIntent); + mController.setSecondWidget(pendingIntent, remoteResponseIntent); // Convert original widgetView into bitmap to use for animation Canvas canvas = new Canvas(bitmap); view.draw(canvas); diff --git a/quickstep/src/com/android/quickstep/util/TabletAppPairLaunchTimings.kt b/quickstep/src/com/android/quickstep/util/TabletAppPairLaunchTimings.kt new file mode 100644 index 0000000000..fb2d63f1ad --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/TabletAppPairLaunchTimings.kt @@ -0,0 +1,23 @@ +/* + * 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 + +/** Timings for the app pair launch animation on tablets. */ +class TabletAppPairLaunchTimings : AppPairLaunchTimings(), SplitAnimationTimings { + override val STAGED_RECT_SLIDE_DURATION = 600 + override fun getDuration() = SplitAnimationTimings.TABLET_APP_PAIR_LAUNCH_DURATION +} diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java index 5d8e53efe7..baaa062e97 100644 --- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java @@ -427,9 +427,16 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { // conflict with layers that WM core positions (ie. the input consumers). For shell // transitions, the animation leashes are reparented to an animation container so we // can bump layers as needed. - builder.setLayer(mDrawsBelowRecents - ? Integer.MIN_VALUE + app.prefixOrderIndex - : ENABLE_SHELL_TRANSITIONS ? Integer.MAX_VALUE : 0); + if (ENABLE_SHELL_TRANSITIONS) { + builder.setLayer(mDrawsBelowRecents + ? Integer.MIN_VALUE + app.prefixOrderIndex + // 1000 is an arbitrary number to give room for multiple layers. + : Integer.MAX_VALUE - 1000 + app.prefixOrderIndex); + } else { + builder.setLayer(mDrawsBelowRecents + ? Integer.MIN_VALUE + app.prefixOrderIndex + : 0); + } } } diff --git a/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt b/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt new file mode 100644 index 0000000000..3a5873ba33 --- /dev/null +++ b/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt @@ -0,0 +1,358 @@ +/* + * 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 android.content.Context +import android.graphics.Canvas +import android.graphics.ColorFilter +import android.graphics.Paint +import android.graphics.PixelFormat +import android.graphics.RectF +import android.graphics.drawable.Drawable +import android.os.Build +import android.view.animation.Interpolator +import com.android.app.animation.Interpolators +import com.android.launcher3.Launcher +import com.android.launcher3.R +import com.android.launcher3.Utilities +import com.android.quickstep.util.AnimUtils +import com.android.systemui.shared.system.QuickStepContract + +/** + * A Drawable that is drawn onto [FloatingAppPairView] every frame during the app pair launch + * animation. Consists of a rectangular background that splits into two, and two app icons that + * increase in size during the animation. + */ +class FloatingAppPairBackground( + context: Context, + private val floatingView: FloatingAppPairView, // the view that we will draw this background on + private val appIcon1: Drawable, + private val appIcon2: Drawable, + dividerPos: Int +) : Drawable() { + companion object { + // Design specs -- app icons start small and expand during the animation + private val STARTING_ICON_SIZE_PX = Utilities.dpToPx(22f) + private val ENDING_ICON_SIZE_PX = Utilities.dpToPx(66f) + + // Null values to use with drawDoubleRoundRect(), since there doesn't seem to be any other + // API for drawing rectangles with 4 different corner radii. + private val EMPTY_RECT = RectF() + private val ARRAY_OF_ZEROES = FloatArray(8) + } + + private val launcher: Launcher + private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG) + + // Animation interpolators + private val expandXInterpolator: Interpolator + private val expandYInterpolator: Interpolator + private val cellSplitInterpolator: Interpolator + private val iconFadeInterpolator: Interpolator + + // Device-specific measurements + private val deviceCornerRadius: Float + private val deviceHalfDividerSize: Float + private val desiredSplitRatio: Float + + init { + launcher = Launcher.getLauncher(context) + val dp = launcher.deviceProfile + // Set up background paint color + val ta = context.theme.obtainStyledAttributes(R.styleable.FolderIconPreview) + backgroundPaint.style = Paint.Style.FILL + backgroundPaint.color = ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0) + ta.recycle() + // Set up timings and interpolators + val timings = AnimUtils.getDeviceAppPairLaunchTimings(launcher.deviceProfile.isTablet) + expandXInterpolator = + Interpolators.clampToProgress( + timings.getStagedRectScaleXInterpolator(), + timings.stagedRectSlideStartOffset, + timings.stagedRectSlideEndOffset + ) + expandYInterpolator = + Interpolators.clampToProgress( + timings.getStagedRectScaleYInterpolator(), + timings.stagedRectSlideStartOffset, + timings.stagedRectSlideEndOffset + ) + cellSplitInterpolator = + Interpolators.clampToProgress( + timings.cellSplitInterpolator, + timings.cellSplitStartOffset, + timings.cellSplitEndOffset + ) + iconFadeInterpolator = + Interpolators.clampToProgress( + timings.iconFadeInterpolator, + timings.iconFadeStartOffset, + timings.iconFadeEndOffset + ) + + // Find device-specific measurements + deviceCornerRadius = QuickStepContract.getWindowCornerRadius(launcher) + deviceHalfDividerSize = + launcher.resources.getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2f + val dividerCenterPos = dividerPos + deviceHalfDividerSize + desiredSplitRatio = + if (dp.isLeftRightSplit) dividerCenterPos / dp.widthPx + else dividerCenterPos / dp.heightPx + } + + override fun draw(canvas: Canvas) { + if (launcher.deviceProfile.isLandscape) { + drawLeftRightSplit(canvas) + } else { + drawTopBottomSplit(canvas) + } + } + + /** When device is in landscape, we draw the rectangles with a left-right split. */ + private fun drawLeftRightSplit(canvas: Canvas) { + val progress = floatingView.progress + + // Since the entire floating app pair surface is scaling up during this animation, we + // scale down most of these drawn elements so that they appear the proper size on-screen. + val scaleFactorX = floatingView.scaleX + val scaleFactorY = floatingView.scaleY + + // Get the bounds where we will draw the background image + val width = bounds.width().toFloat() + val height = bounds.height().toFloat() + + // Get device-specific measurements + val cornerRadiusX = deviceCornerRadius / scaleFactorX + val cornerRadiusY = deviceCornerRadius / scaleFactorY + val halfDividerSize = deviceHalfDividerSize / scaleFactorX + + // Calculate changing measurements for background + // We add one pixel to some measurements to create a smooth edge with no gaps + val onePixel = 1f / scaleFactorX + val changingDividerSize = + (cellSplitInterpolator.getInterpolation(progress) * halfDividerSize) - onePixel + val changingInnerRadiusX = cellSplitInterpolator.getInterpolation(progress) * cornerRadiusX + val changingInnerRadiusY = cellSplitInterpolator.getInterpolation(progress) * cornerRadiusY + val dividerCenterPos = width * desiredSplitRatio + + // The left half of the background image + val leftSide = RectF( + 0f, + 0f, + dividerCenterPos - changingDividerSize, + height + ) + // The right half of the background image + val rightSide = RectF( + dividerCenterPos + changingDividerSize, + 0f, + width, + height + ) + + // Draw background + drawCustomRoundedRect( + canvas, + leftSide, + floatArrayOf( + cornerRadiusX, cornerRadiusY, + changingInnerRadiusX, changingInnerRadiusY, + changingInnerRadiusX, changingInnerRadiusY, + cornerRadiusX, cornerRadiusY + ) + ) + drawCustomRoundedRect( + canvas, + rightSide, + floatArrayOf( + changingInnerRadiusX, changingInnerRadiusY, + cornerRadiusX, cornerRadiusY, + cornerRadiusX, cornerRadiusY, + changingInnerRadiusX, changingInnerRadiusY + ) + ) + + // Calculate changing measurements for icons. + val changingIconSizeX = + (STARTING_ICON_SIZE_PX + + ((ENDING_ICON_SIZE_PX - STARTING_ICON_SIZE_PX) * + expandXInterpolator.getInterpolation(progress))) / scaleFactorX + val changingIconSizeY = + (STARTING_ICON_SIZE_PX + + ((ENDING_ICON_SIZE_PX - STARTING_ICON_SIZE_PX) * + expandYInterpolator.getInterpolation(progress))) / scaleFactorY + + val changingIcon1Left = ((width / 2f - halfDividerSize) / 2f) - (changingIconSizeX / 2f) + val changingIcon2Left = + (width - ((width / 2f - halfDividerSize) / 2f)) - (changingIconSizeX / 2f) + val changingIconTop = (height / 2f) - (changingIconSizeY / 2f) + val changingIconScaleX = changingIconSizeX / appIcon1.bounds.width() + val changingIconScaleY = changingIconSizeY / appIcon1.bounds.height() + val changingIconAlpha = + (255 - (255 * iconFadeInterpolator.getInterpolation(progress))).toInt() + + // Draw first icon + canvas.save() + canvas.translate(changingIcon1Left, changingIconTop) + canvas.scale(changingIconScaleX, changingIconScaleY) + appIcon1.alpha = changingIconAlpha + appIcon1.draw(canvas) + canvas.restore() + + // Draw second icon + canvas.save() + canvas.translate(changingIcon2Left, changingIconTop) + canvas.scale(changingIconScaleX, changingIconScaleY) + appIcon2.alpha = changingIconAlpha + appIcon2.draw(canvas) + canvas.restore() + } + + /** When device is in portrait, we draw the rectangles with a top-bottom split. */ + private fun drawTopBottomSplit(canvas: Canvas) { + val progress = floatingView.progress + + // Since the entire floating app pair surface is scaling up during this animation, we + // scale down most of these drawn elements so that they appear the proper size on-screen. + val scaleFactorX = floatingView.scaleX + val scaleFactorY = floatingView.scaleY + + // Get the bounds where we will draw the background image + val width = bounds.width().toFloat() + val height = bounds.height().toFloat() + + // Get device-specific measurements + val cornerRadiusX = deviceCornerRadius / scaleFactorX + val cornerRadiusY = deviceCornerRadius / scaleFactorY + val halfDividerSize = deviceHalfDividerSize / scaleFactorY + + // Calculate changing measurements for background + // We add one pixel to some measurements to create a smooth edge with no gaps + val onePixel = 1f / scaleFactorY + val changingDividerSize = + (cellSplitInterpolator.getInterpolation(progress) * halfDividerSize) - onePixel + val changingInnerRadiusX = cellSplitInterpolator.getInterpolation(progress) * cornerRadiusX + val changingInnerRadiusY = cellSplitInterpolator.getInterpolation(progress) * cornerRadiusY + val dividerCenterPos = height * desiredSplitRatio + + // The top half of the background image + val topSide = RectF( + 0f, + 0f, + width, + dividerCenterPos - changingDividerSize + ) + // The bottom half of the background image + val bottomSide = RectF( + 0f, + dividerCenterPos + changingDividerSize, + width, + height + ) + + // Draw background + drawCustomRoundedRect( + canvas, + topSide, + floatArrayOf( + cornerRadiusX, cornerRadiusY, + cornerRadiusX, cornerRadiusY, + changingInnerRadiusX, changingInnerRadiusY, + changingInnerRadiusX, changingInnerRadiusY + ) + ) + drawCustomRoundedRect( + canvas, + bottomSide, + floatArrayOf( + changingInnerRadiusX, changingInnerRadiusY, + changingInnerRadiusX, changingInnerRadiusY, + cornerRadiusX, cornerRadiusY, + cornerRadiusX, cornerRadiusY + ) + ) + + // Calculate changing measurements for icons. + val changingIconSizeX = + (STARTING_ICON_SIZE_PX + + ((ENDING_ICON_SIZE_PX - STARTING_ICON_SIZE_PX) * + expandXInterpolator.getInterpolation(progress))) / scaleFactorX + val changingIconSizeY = + (STARTING_ICON_SIZE_PX + + ((ENDING_ICON_SIZE_PX - STARTING_ICON_SIZE_PX) * + expandYInterpolator.getInterpolation(progress))) / scaleFactorY + + val changingIconLeft = (width / 2f) - (changingIconSizeX / 2f) + val changingIcon1Top = (((height / 2f) - halfDividerSize) / 2f) - (changingIconSizeY / 2f) + val changingIcon2Top = + (height - (((height / 2f) - halfDividerSize) / 2f)) - (changingIconSizeY / 2f) + val changingIconScaleX = changingIconSizeX / appIcon1.bounds.width() + val changingIconScaleY = changingIconSizeY / appIcon1.bounds.height() + val changingIconAlpha = + (255 - 255 * iconFadeInterpolator.getInterpolation(progress)).toInt() + + // Draw first icon + canvas.save() + canvas.translate(changingIconLeft, changingIcon1Top) + canvas.scale(changingIconScaleX, changingIconScaleY) + appIcon1.alpha = changingIconAlpha + appIcon1.draw(canvas) + canvas.restore() + + // Draw second icon + canvas.save() + canvas.translate(changingIconLeft, changingIcon2Top) + canvas.scale(changingIconScaleX, changingIconScaleY) + appIcon2.alpha = changingIconAlpha + appIcon2.draw(canvas) + canvas.restore() + } + + /** + * Draws a rectangle with custom rounded corners. + * + * @param c The Canvas to draw on. + * @param rect The bounds of the rectangle. + * @param radii An array of 8 radii for the corners: top left x, top left y, top right x, top + * right y, bottom right x, and so on. + */ + private fun drawCustomRoundedRect(c: Canvas, rect: RectF, radii: FloatArray) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + // Canvas.drawDoubleRoundRect is supported from Q onward + c.drawDoubleRoundRect(rect, radii, EMPTY_RECT, ARRAY_OF_ZEROES, backgroundPaint) + } else { + // Fallback rectangle with uniform rounded corners + val scaleFactorX = floatingView.scaleX + val scaleFactorY = floatingView.scaleY + val cornerRadiusX = QuickStepContract.getWindowCornerRadius(launcher) / scaleFactorX + val cornerRadiusY = QuickStepContract.getWindowCornerRadius(launcher) / scaleFactorY + c.drawRoundRect(rect, cornerRadiusX, cornerRadiusY, backgroundPaint) + } + } + + override fun getOpacity(): Int { + return PixelFormat.OPAQUE + } + + override fun setAlpha(i: Int) { + // Required by Drawable but not used. + } + + override fun setColorFilter(colorFilter: ColorFilter?) { + // Required by Drawable but not used. + } +} diff --git a/quickstep/src/com/android/quickstep/views/FloatingAppPairView.kt b/quickstep/src/com/android/quickstep/views/FloatingAppPairView.kt new file mode 100644 index 0000000000..e90aa13d97 --- /dev/null +++ b/quickstep/src/com/android/quickstep/views/FloatingAppPairView.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.quickstep.views + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Rect +import android.graphics.RectF +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import com.android.launcher3.R +import com.android.launcher3.Utilities +import com.android.launcher3.statemanager.StatefulActivity +import com.android.launcher3.views.BaseDragLayer + +/** + * A temporary View that is created for the app pair launch animation and destroyed at the end. + * Matches the size & position of the app pair icon graphic, and expands to full screen. + */ +class FloatingAppPairView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : + FrameLayout(context, attrs) { + companion object { + fun getFloatingAppPairView( + launcher: StatefulActivity<*>, + originalView: View, + appIcon1: Drawable, + appIcon2: Drawable, + dividerPos: Int + ): FloatingAppPairView { + val dragLayer: ViewGroup = launcher.getDragLayer() + val floatingView = + launcher + .getLayoutInflater() + .inflate(R.layout.floating_app_pair_view, dragLayer, false) + as FloatingAppPairView + floatingView.init(launcher, originalView, appIcon1, appIcon2, dividerPos) + dragLayer.addView(floatingView, dragLayer.childCount - 1) + return floatingView + } + } + + val startingPosition = RectF() + private lateinit var background: FloatingAppPairBackground + var progress = 0f + + /** Initializes the view, copying the bounds and location of the original icon view. */ + fun init( + launcher: StatefulActivity<*>, + originalView: View, + appIcon1: Drawable, + appIcon2: Drawable, + dividerPos: Int + ) { + val viewBounds = Rect(0, 0, originalView.width, originalView.height) + Utilities.getBoundsForViewInDragLayer( + launcher.getDragLayer(), + originalView, + viewBounds, + false /* ignoreTransform */, + null /* recycle */, + startingPosition + ) + val lp = + BaseDragLayer.LayoutParams( + Math.round(startingPosition.width()), + Math.round(startingPosition.height()) + ) + lp.ignoreInsets = true + + // Position the floating view exactly on top of the original + lp.topMargin = Math.round(startingPosition.top) + lp.leftMargin = Math.round(startingPosition.left) + + layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin + lp.height) + layoutParams = lp + + // Prepare to draw app pair icon background + background = FloatingAppPairBackground(context, this, appIcon1, appIcon2, dividerPos) + background.setBounds(0, 0, lp.width, lp.height) + } + + override fun dispatchDraw(canvas: Canvas) { + super.dispatchDraw(canvas) + background.draw(canvas) + } +} diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 7e1034b16a..87cee63f9e 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -2124,8 +2124,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T for (int i = 0; i < taskCount; i++) { TaskView taskView = requireTaskViewAt(i); taskView.updateTaskSize(); - taskView.getPrimaryNonGridTranslationProperty().set(taskView, accumulatedTranslationX); - taskView.getSecondaryNonGridTranslationProperty().set(taskView, 0f); + taskView.setNonGridTranslationX(accumulatedTranslationX); taskView.setNonGridPivotTranslationX(translateXToMiddle); // Compensate space caused by TaskView scaling. float widthDiff = @@ -2642,23 +2641,25 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T if (endState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile())) { TaskView runningTaskView = getRunningTaskView(); float runningTaskPrimaryGridTranslation = 0; + float runningTaskSecondaryGridTranslation = 0; if (runningTaskView != null) { // Apply the grid translation to running task unless it's being snapped to // and removes the current translation applied to the running task. - runningTaskPrimaryGridTranslation = mOrientationHandler.getPrimaryValue( - runningTaskView.getGridTranslationX(), - runningTaskView.getGridTranslationY()) - - runningTaskView.getPrimaryNonGridTranslationProperty().get( - runningTaskView); + runningTaskPrimaryGridTranslation = runningTaskView.getGridTranslationX() + - runningTaskView.getNonGridTranslationX(); + runningTaskSecondaryGridTranslation = runningTaskView.getGridTranslationY(); } for (TaskViewSimulator tvs : taskViewSimulators) { if (animatorSet == null) { setGridProgress(1); tvs.taskPrimaryTranslation.value = runningTaskPrimaryGridTranslation; + tvs.taskSecondaryTranslation.value = runningTaskSecondaryGridTranslation; } else { animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1)); animatorSet.play(tvs.taskPrimaryTranslation.animateToValue( runningTaskPrimaryGridTranslation)); + animatorSet.play(tvs.taskSecondaryTranslation.animateToValue( + runningTaskSecondaryGridTranslation)); } } } @@ -3123,6 +3124,14 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T + snappedTaskNonGridScrollAdjustment); } + final TaskView runningTask = getRunningTaskView(); + if (showAsGrid() && enableGridOnlyOverview() && runningTask != null) { + runActionOnRemoteHandles( + remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator() + .taskSecondaryTranslation.value = runningTask.getGridTranslationY() + ); + } + mClearAllButton.setGridTranslationPrimary( clearAllTotalTranslationX - snappedTaskGridTranslationX); mClearAllButton.setGridScrollOffset( diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index af4f402f07..66a880b1f1 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -119,6 +119,8 @@ import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; +import kotlin.Unit; + import java.lang.annotation.Retention; import java.util.Arrays; import java.util.Collections; @@ -127,8 +129,6 @@ import java.util.List; import java.util.function.Consumer; import java.util.stream.Stream; -import kotlin.Unit; - /** * A task in the Recents view. */ @@ -304,32 +304,6 @@ public class TaskView extends FrameLayout implements Reusable { } }; - private static final FloatProperty<TaskView> NON_GRID_TRANSLATION_X = - new FloatProperty<TaskView>("nonGridTranslationX") { - @Override - public void setValue(TaskView taskView, float v) { - taskView.setNonGridTranslationX(v); - } - - @Override - public Float get(TaskView taskView) { - return taskView.mNonGridTranslationX; - } - }; - - private static final FloatProperty<TaskView> NON_GRID_TRANSLATION_Y = - new FloatProperty<TaskView>("nonGridTranslationY") { - @Override - public void setValue(TaskView taskView, float v) { - taskView.setNonGridTranslationY(v); - } - - @Override - public Float get(TaskView taskView) { - return taskView.mNonGridTranslationY; - } - }; - public static final FloatProperty<TaskView> GRID_END_TRANSLATION_X = new FloatProperty<TaskView>("gridEndTranslationX") { @Override @@ -386,7 +360,6 @@ public class TaskView extends FrameLayout implements Reusable { // Applied as a complement to gridTranslation, for adjusting the carousel overview and quick // switch. private float mNonGridTranslationX; - private float mNonGridTranslationY; private float mNonGridPivotTranslationX; // Used when in SplitScreenSelectState private float mSplitSelectTranslationY; @@ -1062,9 +1035,6 @@ 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 @@ -1323,7 +1293,7 @@ public class TaskView extends FrameLayout implements Reusable { } protected void resetPersistentViewTransforms() { - mNonGridTranslationX = mNonGridTranslationY = mGridTranslationX = + mNonGridTranslationX = mGridTranslationX = mGridTranslationY = mBoxTranslationY = mNonGridPivotTranslationX = 0f; resetViewTransforms(); } @@ -1494,14 +1464,16 @@ public class TaskView extends FrameLayout implements Reusable { applyTranslationY(); } - private void setNonGridTranslationX(float nonGridTranslationX) { - mNonGridTranslationX = nonGridTranslationX; - applyTranslationX(); + public float getNonGridTranslationX() { + return mNonGridTranslationX; } - private void setNonGridTranslationY(float nonGridTranslationY) { - mNonGridTranslationY = nonGridTranslationY; - applyTranslationY(); + /** + * Updates X coordinate of non-grid translation. + */ + public void setNonGridTranslationX(float nonGridTranslationX) { + mNonGridTranslationX = nonGridTranslationX; + applyTranslationX(); } public void setGridTranslationX(float gridTranslationX) { @@ -1540,7 +1512,7 @@ public class TaskView extends FrameLayout implements Reusable { if (gridEnabled) { scrollAdjustment += mGridTranslationX; } else { - scrollAdjustment += getPrimaryNonGridTranslationProperty().get(this); + scrollAdjustment += getNonGridTranslationX(); } return scrollAdjustment; } @@ -1586,9 +1558,7 @@ public class TaskView extends FrameLayout implements Reusable { * change according to a temporary state (e.g. task offset). */ public float getPersistentTranslationY() { - return mBoxTranslationY - + getNonGridTrans(mNonGridTranslationY) - + getGridTrans(mGridTranslationY); + return mBoxTranslationY + getGridTrans(mGridTranslationY); } public FloatProperty<TaskView> getPrimarySplitTranslationProperty() { @@ -1626,16 +1596,6 @@ public class TaskView extends FrameLayout implements Reusable { TASK_RESISTANCE_TRANSLATION_X, TASK_RESISTANCE_TRANSLATION_Y); } - public FloatProperty<TaskView> getPrimaryNonGridTranslationProperty() { - return getPagedOrientationHandler().getPrimaryValue( - NON_GRID_TRANSLATION_X, NON_GRID_TRANSLATION_Y); - } - - public FloatProperty<TaskView> getSecondaryNonGridTranslationProperty() { - return getPagedOrientationHandler().getSecondaryValue( - NON_GRID_TRANSLATION_X, NON_GRID_TRANSLATION_Y); - } - @Override public boolean hasOverlappingRendering() { // TODO: Clip-out the icon region from the thumbnail, since they are overlapping. diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java index eced5a9a41..8d54dce8ef 100644 --- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java +++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java @@ -144,7 +144,7 @@ public class FallbackRecentsTest { .around(new TestStabilityRule()) .around(new NavigationModeSwitchRule(mLauncher)) .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData)) - .around(viewCaptureRule) + // .around(viewCaptureRule) b/315482167 .around(new TestIsolationRule(mLauncher, false)) .around(setLauncherCommand); diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java index 1129a337e3..728fe6741d 100644 --- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java +++ b/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java @@ -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.quickstep; import static androidx.test.InstrumentationRegistry.getInstrumentation; @@ -27,7 +42,7 @@ import java.time.Duration; @LargeTest @RunWith(AndroidJUnit4.class) -public class DigitalWellBeingToastTest extends AbstractQuickStepTest { +public class TaplDigitalWellBeingToastTest extends AbstractQuickStepTest { private static final String CALCULATOR_PACKAGE = resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR); diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/TaplStartLauncherViaGestureTests.java index 85440e99be..7e274e8833 100644 --- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java +++ b/quickstep/tests/src/com/android/quickstep/TaplStartLauncherViaGestureTests.java @@ -32,7 +32,7 @@ import org.junit.runner.RunWith; @LargeTest @RunWith(AndroidJUnit4.class) -public class StartLauncherViaGestureTests extends AbstractQuickStepTest { +public class TaplStartLauncherViaGestureTests extends AbstractQuickStepTest { static final int STRESS_REPEAT_COUNT = 10; diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java index 9a2826d16b..c9e536a737 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java @@ -20,9 +20,10 @@ import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; +import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -32,17 +33,10 @@ public class TaplTestsPersistentTaskbar extends AbstractTaplTestsTaskbar { @Test @TaskbarModeSwitch(mode = PERSISTENT) - public void testHideShowTaskbar() { - getTaskbar().hide(); - mLauncher.getLaunchedAppState().showTaskbar(); - } - - @Test - @TaskbarModeSwitch(mode = PERSISTENT) - @Ignore // b/301575789 - public void testHideTaskbarPersistsOnRecreate() { - getTaskbar().hide(); - mLauncher.recreateTaskbar(); - mLauncher.getLaunchedAppState().assertTaskbarHidden(); + @PortraitLandscape + @NavigationModeSwitch + public void testTaskbarFillsWidth() { + // Width check is performed inside TAPL whenever getTaskbar() is called. + getTaskbar(); } } diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index b3cc215abc..6cbe1712e0 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -18,7 +18,6 @@ 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 com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT; import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT; import static org.junit.Assert.assertEquals; @@ -295,7 +294,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { } @Test - @TaskbarModeSwitch(mode = PERSISTENT) + @TaskbarModeSwitch + @Ignore // b/314873201 public void testQuickSwitchToPreviousAppForTablet() throws Exception { assumeTrue(mLauncher.isTablet()); startTestActivity(2); @@ -304,22 +304,33 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { // Set ignoreTaskbarVisibility to true to verify the task bar visibility explicitly. mLauncher.setIgnoreTaskbarVisibility(true); - // Expect task bar invisible when the launched app was the IME activity. - LaunchedAppState launchedAppState = getAndAssertLaunchedApp(); - if (isHardwareKeyboard()) { - launchedAppState.assertTaskbarVisible(); - } else { - launchedAppState.assertTaskbarHidden(); - } - // Quick-switch to the test app with swiping to right. - quickSwitchToPreviousAppAndAssert(true /* toRight */); + try { + boolean isTransientTaskbar = mLauncher.isTransientTaskbar(); + // Expect task bar invisible when the launched app was the IME activity. + LaunchedAppState launchedAppState = getAndAssertLaunchedApp(); + if (!isTransientTaskbar && isHardwareKeyboard()) { + launchedAppState.assertTaskbarVisible(); + } else { + launchedAppState.assertTaskbarHidden(); + } + + // Quick-switch to the test app with swiping to right. + quickSwitchToPreviousAppAndAssert(true /* toRight */); - 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(); + assertTestActivityIsRunning(2, + "The first app we should have quick switched to is not running"); + launchedAppState = getAndAssertLaunchedApp(); + if (isTransientTaskbar) { + launchedAppState.assertTaskbarHidden(); + } else { + // Expect taskbar visible when the launched app was the test activity. + launchedAppState.assertTaskbarVisible(); + } + } finally { + // Reset ignoreTaskbarVisibility to ensure other tests still verify it. + mLauncher.setIgnoreTaskbarVisibility(false); + } } private boolean isHardwareKeyboard() { @@ -344,6 +355,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { public void testPressBack() throws Exception { InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( READ_DEVICE_CONFIG_PERMISSION); + // Debug if we need to goHome to prevent wrong previous state b/315525621 + mLauncher.goHome(); assumeFalse(FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get()); mLauncher.getWorkspace().switchToAllApps(); mLauncher.pressBack(); @@ -358,7 +371,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @PortraitLandscape - @TaskbarModeSwitch(mode = PERSISTENT) + @TaskbarModeSwitch() @PlatinumTest(focusArea = "launcher") @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/309820115 @ScreenRecord // b/309820115 @@ -454,6 +467,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @PortraitLandscape + @TaskbarModeSwitch public void testTaskbarDeadzonesForTablet() throws Exception { assumeTrue(mLauncher.isTablet()); @@ -466,15 +480,29 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { launcher -> assertTrue("Should have at least 3 tasks", getTaskCount(launcher) >= 3)); - // On persistent taskbar, it should not dismiss when tapping the taskbar - overview.touchTaskbarBottomCorner(/* tapRight= */ false); - assertTrue("Launcher internal state should be Overview", - isInState(() -> LauncherState.OVERVIEW)); + if (mLauncher.isTransientTaskbar()) { + // On transient taskbar, it should dismiss when tapping outside taskbar bounds. + overview.touchTaskbarBottomCorner(/* tapRight= */ false); + assertTrue("Launcher internal state should be Normal", + isInState(() -> LauncherState.NORMAL)); - // On persistent taskbar, it should not dismiss when tapping the taskbar - overview.touchTaskbarBottomCorner(/* tapRight= */ true); - assertTrue("Launcher internal state should be Overview", - isInState(() -> LauncherState.OVERVIEW)); + overview = mLauncher.getWorkspace().switchToOverview(); + + // On transient taskbar, it should dismiss when tapping outside taskbar bounds. + overview.touchTaskbarBottomCorner(/* tapRight= */ true); + assertTrue("Launcher internal state should be Normal", + isInState(() -> LauncherState.NORMAL)); + } else { + // On persistent taskbar, it should not dismiss when tapping the taskbar + overview.touchTaskbarBottomCorner(/* tapRight= */ false); + assertTrue("Launcher internal state should be Overview", + isInState(() -> LauncherState.OVERVIEW)); + + // On persistent taskbar, it should not dismiss when tapping the taskbar + overview.touchTaskbarBottomCorner(/* tapRight= */ true); + assertTrue("Launcher internal state should be Overview", + isInState(() -> LauncherState.OVERVIEW)); + } } @Test diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java index 0eec8b7a37..3465f23476 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java @@ -86,7 +86,7 @@ public class TaplTestsTrackpad extends AbstractQuickStepTest { mLauncher.setTrackpadGestureType(TrackpadGestureType.THREE_FINGER); startTestActivity(2); - mLauncher.pressBack(); + mLauncher.getLaunchedAppState().pressBackToWorkspace(); } finally { instrumentation.getUiAutomation().dropShellPermissionIdentity(); } diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java index db23cc04c4..7109bbff54 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java @@ -24,6 +24,7 @@ import static org.junit.Assume.assumeTrue; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch; import org.junit.Test; @@ -64,4 +65,12 @@ public class TaplTestsTransientTaskbar extends AbstractTaplTestsTaskbar { getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE); mLauncher.getLaunchedAppState().clickStashedTaskbarToGoHome(); } + + @Test + @TaskbarModeSwitch(mode = TRANSIENT) + @PortraitLandscape + public void testSwipeToStashAndUnstash() { + getTaskbar().swipeDownToStash(); + mLauncher.getLaunchedAppState().swipeUpToUnstashTaskbar(); + } } diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/TaplViewInflationDuringSwipeUp.java index 2318f5486d..f76e17aa27 100644 --- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java +++ b/quickstep/tests/src/com/android/quickstep/TaplViewInflationDuringSwipeUp.java @@ -85,7 +85,7 @@ import java.util.function.IntConsumer; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest { +public class TaplViewInflationDuringSwipeUp extends AbstractQuickStepTest { private SparseArray<ViewConfiguration> mConfigMap; private InitTracker mInitTracker; diff --git a/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt b/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt new file mode 100644 index 0000000000..dbe4624e45 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt @@ -0,0 +1,202 @@ +/* + * 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.taskbar.controllers + +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.launcher3.LauncherPrefs +import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING +import com.android.launcher3.logging.StatsLogManager +import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE +import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN +import com.android.launcher3.taskbar.TaskbarActivityContext +import com.android.launcher3.taskbar.TaskbarBaseTestCase +import com.android.launcher3.taskbar.TaskbarDividerPopupView +import com.android.launcher3.taskbar.TaskbarDragLayer +import com.android.launcher3.taskbar.TaskbarPinningController +import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_PERSISTENT +import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_TRANSIENT +import com.android.launcher3.taskbar.TaskbarSharedState +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doNothing +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.spy +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(AndroidJUnit4::class) +class TaskbarPinningControllerTest : TaskbarBaseTestCase() { + private val taskbarDragLayer = mock<TaskbarDragLayer>() + private val taskbarSharedState = mock<TaskbarSharedState>() + private val launcherPrefs = mock<LauncherPrefs> { on { get(TASKBAR_PINNING) } doReturn false } + private val statsLogger = mock<StatsLogManager.StatsLogger>() + private val statsLogManager = mock<StatsLogManager> { on { logger() } doReturn statsLogger } + private lateinit var pinningController: TaskbarPinningController + + @Before + override fun setup() { + super.setup() + whenever(taskbarActivityContext.launcherPrefs).thenReturn(launcherPrefs) + whenever(taskbarActivityContext.dragLayer).thenReturn(taskbarDragLayer) + whenever(taskbarActivityContext.statsLogManager).thenReturn(statsLogManager) + pinningController = spy(TaskbarPinningController(taskbarActivityContext)) + pinningController.init(taskbarControllers, taskbarSharedState) + } + + @Test + fun testOnCloseCallback_whenClosingPopupView_shouldLogStatsForClosingPopupMenu() { + pinningController.onCloseCallback(false) + verify(statsLogger, times(1)).log(LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE) + } + + @Test + fun testOnCloseCallback_whenClosingPopupView_shouldPostVisibilityChangedToDragLayer() { + val argumentCaptor = argumentCaptor<Runnable>() + pinningController.onCloseCallback(false) + verify(taskbarDragLayer, times(1)).post(argumentCaptor.capture()) + + val runnable = argumentCaptor.lastValue + assertThat(runnable).isNotNull() + + runnable.run() + verify(taskbarActivityContext, times(1)).onPopupVisibilityChanged(false) + } + + @Test + fun testOnCloseCallback_whenPreferenceUnchanged_shouldNotAnimateTaskbarPinning() { + pinningController.onCloseCallback(false) + verify(taskbarSharedState, never()).taskbarWasPinned = true + verify(pinningController, never()).animateTaskbarPinning(any()) + } + + @Test + fun testOnCloseCallback_whenPreferenceChanged_shouldAnimateToPinnedTaskbar() { + whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false) + doNothing().whenever(pinningController).animateTaskbarPinning(any()) + + pinningController.onCloseCallback(true) + + verify(taskbarSharedState, times(1)).taskbarWasPinned = false + verify(pinningController, times(1)).animateTaskbarPinning(PINNING_PERSISTENT) + } + + @Test + fun testOnCloseCallback_whenPreferenceChanged_shouldAnimateToTransientTaskbar() { + whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(true) + doNothing().whenever(pinningController).animateTaskbarPinning(any()) + + pinningController.onCloseCallback(true) + + verify(taskbarSharedState, times(1)).taskbarWasPinned = true + verify(pinningController, times(1)).animateTaskbarPinning(PINNING_TRANSIENT) + } + + @Test + fun testShowPinningView_whenShowingPinningView_shouldSetTaskbarWindowFullscreenAndPostRunnableToView() { + val popupView = + mock<TaskbarDividerPopupView<TaskbarActivityContext>> { + on { requestFocus() } doReturn true + } + val view = mock<View>() + val argumentCaptor = argumentCaptor<Runnable>() + doReturn(popupView).whenever(pinningController).getPopupView(view) + + pinningController.showPinningView(view) + + verify(view, times(1)).post(argumentCaptor.capture()) + + val runnable = argumentCaptor.lastValue + assertThat(runnable).isNotNull() + runnable.run() + + verify(pinningController, times(1)).getPopupView(view) + verify(popupView, times(1)).requestFocus() + verify(popupView, times(1)).onCloseCallback = any() + verify(taskbarActivityContext, times(1)).onPopupVisibilityChanged(true) + verify(popupView, times(1)).show() + verify(statsLogger, times(1)).log(LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN) + } + + @Test + fun testAnimateTaskbarPinning_whenAnimationEnds_shouldInvokeCallbackDoOnEnd() { + val animatorSet = spy(AnimatorSet()) + doReturn(animatorSet) + .whenever(pinningController) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_PERSISTENT) + doNothing().whenever(animatorSet).start() + pinningController.animateTaskbarPinning(PINNING_PERSISTENT) + animatorSet.listeners[0].onAnimationEnd(ObjectAnimator()) + verify(pinningController, times(1)).recreateTaskbarAndUpdatePinningValue() + } + + @Test + fun testAnimateTaskbarPinning_whenAnimatingToPersistentTaskbar_shouldAnimateToPinnedTaskbar() { + val animatorSet = spy(AnimatorSet()) + doReturn(animatorSet) + .whenever(pinningController) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_PERSISTENT) + doNothing().whenever(animatorSet).start() + pinningController.animateTaskbarPinning(PINNING_PERSISTENT) + + verify(taskbarOverlayController, times(1)).hideWindow() + verify(pinningController, times(1)) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_PERSISTENT) + verify(taskbarViewController, times(1)) + .animateAwayNotificationDotsDuringTaskbarPinningAnimation() + verify(taskbarDragLayer, times(1)).setAnimatingTaskbarPinning(true) + assertThat(pinningController.isAnimatingTaskbarPinning).isTrue() + assertThat(animatorSet.listeners).isNotNull() + } + + @Test + fun testAnimateTaskbarPinning_whenAnimatingToTransientTaskbar_shouldAnimateToTransientTaskbar() { + val animatorSet = spy(AnimatorSet()) + doReturn(animatorSet) + .whenever(pinningController) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_TRANSIENT) + doNothing().whenever(animatorSet).start() + pinningController.animateTaskbarPinning(PINNING_TRANSIENT) + + verify(taskbarOverlayController, times(1)).hideWindow() + verify(pinningController, times(1)) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_TRANSIENT) + verify(taskbarDragLayer, times(1)).setAnimatingTaskbarPinning(true) + assertThat(pinningController.isAnimatingTaskbarPinning).isTrue() + verify(taskbarViewController, times(1)) + .animateAwayNotificationDotsDuringTaskbarPinningAnimation() + assertThat(animatorSet.listeners).isNotNull() + } + + @Test + fun testRecreateTaskbarAndUpdatePinningValue_whenAnimationEnds_shouldUpdateTaskbarPinningLauncherPref() { + pinningController.recreateTaskbarAndUpdatePinningValue() + verify(taskbarDragLayer, times(1)).setAnimatingTaskbarPinning(false) + assertThat(pinningController.isAnimatingTaskbarPinning).isFalse() + verify(launcherPrefs, times(1)).put(TASKBAR_PINNING, true) + } +} diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt index 50803fe3f4..86018b1783 100644 --- a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt +++ b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt @@ -19,8 +19,13 @@ package com.android.quickstep.util import android.graphics.Bitmap import android.graphics.drawable.Drawable +import android.view.SurfaceControl.Transaction import android.view.View +import android.window.TransitionInfo import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.launcher3.apppairs.AppPairIcon +import com.android.launcher3.statehandlers.DepthController +import com.android.launcher3.statemanager.StateManager import com.android.launcher3.util.SplitConfigurationOptions import com.android.quickstep.views.GroupedTaskView import com.android.quickstep.views.IconView @@ -32,13 +37,18 @@ import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doNothing import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class SplitAnimationControllerTest { private val taskId = 9 + private val taskId2 = 10 private val mockSplitSelectStateController: SplitSelectStateController = mock() // TaskView @@ -52,12 +62,19 @@ class SplitAnimationControllerTest { private val mockTask: Task = mock() private val mockTaskKey: Task.TaskKey = mock() private val mockTaskIdAttributeContainer: TaskIdAttributeContainer = mock() + // AppPairIcon + private val mockAppPairIcon: AppPairIcon = mock() // SplitSelectSource private val splitSelectSource: SplitConfigurationOptions.SplitSelectSource = mock() private val mockSplitSourceDrawable: Drawable = mock() private val mockSplitSourceView: View = mock() + private val stateManager: StateManager<*> = mock() + private val depthController: DepthController = mock() + private val transitionInfo: TransitionInfo = mock() + private val transaction: Transaction = mock() + lateinit var splitAnimationController: SplitAnimationController @Before @@ -172,4 +189,110 @@ class SplitAnimationControllerTest { splitAnimInitProps.iconDrawable ) } + + @Test + fun playsAppropriateSplitLaunchAnimation_playsLegacyLaunchCorrectly() { + val spySplitAnimationController = spy(splitAnimationController) + doNothing() + .whenever(spySplitAnimationController) + .composeRecentsSplitLaunchAnimatorLegacy( + any(), any(), any(), any(), any(), any(), any(), any(), any()) + + spySplitAnimationController.playSplitLaunchAnimation( + mockGroupedTaskView, + null /* launchingIconView */, + taskId, + taskId2, + arrayOf() /* apps */, + arrayOf() /* wallpapers */, + arrayOf() /* nonApps */, + stateManager, + depthController, + null /* info */, + null /* t */, + {} /* finishCallback */ + ) + + verify(spySplitAnimationController) + .composeRecentsSplitLaunchAnimatorLegacy( + any(), any(), any(), any(), any(), any(), any(), any(), any()) + } + + @Test + fun playsAppropriateSplitLaunchAnimation_playsRecentsLaunchCorrectly() { + val spySplitAnimationController = spy(splitAnimationController) + doNothing() + .whenever(spySplitAnimationController) + .composeRecentsSplitLaunchAnimator(any(), any(), any(), any(), any(), any()) + + spySplitAnimationController.playSplitLaunchAnimation( + mockGroupedTaskView, + null /* launchingIconView */, + taskId, + taskId2, + null /* apps */, + null /* wallpapers */, + null /* nonApps */, + stateManager, + depthController, + transitionInfo, + transaction, + {} /* finishCallback */ + ) + + verify(spySplitAnimationController) + .composeRecentsSplitLaunchAnimator(any(), any(), any(), any(), any(), any()) + } + + @Test + fun playsAppropriateSplitLaunchAnimation_playsIconLaunchCorrectly() { + val spySplitAnimationController = spy(splitAnimationController) + doNothing() + .whenever(spySplitAnimationController) + .composeIconSplitLaunchAnimator(any(), any(), any(), any(), any(), any()) + + spySplitAnimationController.playSplitLaunchAnimation( + null /* launchingTaskView */, + mockAppPairIcon, + taskId, + taskId2, + null /* apps */, + null /* wallpapers */, + null /* nonApps */, + stateManager, + depthController, + transitionInfo, + transaction, + {} /* finishCallback */ + ) + + verify(spySplitAnimationController) + .composeIconSplitLaunchAnimator(any(), any(), any(), any(), any(), any()) + } + + @Test + fun playsAppropriateSplitLaunchAnimation_playsFadeInLaunchCorrectly() { + val spySplitAnimationController = spy(splitAnimationController) + doNothing() + .whenever(spySplitAnimationController) + .composeFadeInSplitLaunchAnimator(any(), any(), any(), any(), any()) + + spySplitAnimationController.playSplitLaunchAnimation( + null /* launchingTaskView */, + null /* launchingIconView */, + taskId, + taskId2, + null /* apps */, + null /* wallpapers */, + null /* nonApps */, + stateManager, + depthController, + transitionInfo, + transaction, + {} /* finishCallback */ + ) + + verify(spySplitAnimationController) + .composeFadeInSplitLaunchAnimator(any(), any(), any(), any(), any()) + } } diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt index f292f9aa85..f41ac4809b 100644 --- a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt +++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt @@ -114,6 +114,7 @@ class SplitSelectStateControllerTest { argumentCaptor<Consumer<ArrayList<GroupTask>>> { splitSelectStateController.findLastActiveTasksAndRunCallback( listOf(nonMatchingComponent), + false /* findExactPairMatch */, taskConsumer ) verify(recentsModel).getTasks(capture()) @@ -166,6 +167,7 @@ class SplitSelectStateControllerTest { argumentCaptor<Consumer<ArrayList<GroupTask>>> { splitSelectStateController.findLastActiveTasksAndRunCallback( listOf(matchingComponent), + false /* findExactPairMatch */, taskConsumer ) verify(recentsModel).getTasks(capture()) @@ -206,6 +208,7 @@ class SplitSelectStateControllerTest { argumentCaptor<Consumer<ArrayList<GroupTask>>> { splitSelectStateController.findLastActiveTasksAndRunCallback( listOf(nonPrimaryUserComponent), + false /* findExactPairMatch */, taskConsumer ) verify(recentsModel).getTasks(capture()) @@ -261,6 +264,7 @@ class SplitSelectStateControllerTest { argumentCaptor<Consumer<ArrayList<GroupTask>>> { splitSelectStateController.findLastActiveTasksAndRunCallback( listOf(nonPrimaryUserComponent), + false /* findExactPairMatch */, taskConsumer ) verify(recentsModel).getTasks(capture()) @@ -313,6 +317,7 @@ class SplitSelectStateControllerTest { argumentCaptor<Consumer<ArrayList<GroupTask>>> { splitSelectStateController.findLastActiveTasksAndRunCallback( listOf(matchingComponent), + false /* findExactPairMatch */, taskConsumer ) verify(recentsModel).getTasks(capture()) @@ -366,6 +371,7 @@ class SplitSelectStateControllerTest { argumentCaptor<Consumer<ArrayList<GroupTask>>> { splitSelectStateController.findLastActiveTasksAndRunCallback( listOf(nonMatchingComponent, matchingComponent), + false /* findExactPairMatch */, taskConsumer ) verify(recentsModel).getTasks(capture()) @@ -418,6 +424,7 @@ class SplitSelectStateControllerTest { argumentCaptor<Consumer<ArrayList<GroupTask>>> { splitSelectStateController.findLastActiveTasksAndRunCallback( listOf(matchingComponent, matchingComponent), + false /* findExactPairMatch */, taskConsumer ) verify(recentsModel).getTasks(capture()) @@ -483,6 +490,59 @@ class SplitSelectStateControllerTest { argumentCaptor<Consumer<ArrayList<GroupTask>>> { splitSelectStateController.findLastActiveTasksAndRunCallback( listOf(matchingComponent, matchingComponent), + false /* findExactPairMatch */, + taskConsumer + ) + verify(recentsModel).getTasks(capture()) + } + .lastValue + + // Send our mocked tasks + consumer.accept(tasks) + } + + @Test + fun activeTasks_multipleSearchShouldFindExactPairMatch() { + val matchingPackage = "hotdog" + val matchingClass = "juice" + val matchingComponent = + ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle) + val matchingPackage2 = "pomegranate" + val matchingClass2 = "juice" + val matchingComponent2 = + ComponentKey(ComponentName(matchingPackage2, matchingClass2), primaryUserHandle) + + val groupTask1 = + generateGroupTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie")) + val groupTask2 = + generateGroupTask( + ComponentName(matchingPackage2, matchingClass2), + ComponentName(matchingPackage, matchingClass) + ) + val groupTask3 = + generateGroupTask( + ComponentName("hotdog", "pie"), + ComponentName(matchingPackage, matchingClass) + ) + val tasks: ArrayList<GroupTask> = ArrayList() + tasks.add(groupTask3) + 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 1", 1, it.size) + assertEquals("Found wrong task", it[0], groupTask2.task1) + } + + // Capture callback from recentsModel#getTasks() + val consumer = + argumentCaptor<Consumer<ArrayList<GroupTask>>> { + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(matchingComponent2, matchingComponent), + true /* findExactPairMatch */, taskConsumer ) verify(recentsModel).getTasks(capture()) diff --git a/res/layout/add_item_confirmation_activity.xml b/res/layout/add_item_confirmation_activity.xml index e29e1b1fef..d113a38ac8 100644 --- a/res/layout/add_item_confirmation_activity.xml +++ b/res/layout/add_item_confirmation_activity.xml @@ -16,7 +16,7 @@ ** limitations under the License. */ --> -<com.android.launcher3.dragndrop.AddItemDragLayer +<com.android.launcher3.dragndrop.SimpleDragLayer xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/add_item_drag_layer" android:layout_width="match_parent" @@ -121,6 +121,6 @@ </LinearLayout> </com.android.launcher3.widget.AddItemWidgetsBottomSheet> -</com.android.launcher3.dragndrop.AddItemDragLayer> +</com.android.launcher3.dragndrop.SimpleDragLayer> diff --git a/res/layout/app_pair_icon.xml b/res/layout/app_pair_icon.xml index 2b9a98b037..4e2dd58bc3 100644 --- a/res/layout/app_pair_icon.xml +++ b/res/layout/app_pair_icon.xml @@ -20,6 +20,11 @@ android:layout_height="match_parent" android:orientation="vertical" android:focusable="true" > + <com.android.launcher3.apppairs.AppPairIconGraphic + android:id="@+id/app_pair_icon_graphic" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:focusable="false" /> <com.android.launcher3.views.DoubleShadowBubbleTextView style="@style/BaseIcon.Workspace" android:id="@+id/app_pair_icon_name" diff --git a/res/layout/floating_app_pair_view.xml b/res/layout/floating_app_pair_view.xml new file mode 100644 index 0000000000..88ec6552b0 --- /dev/null +++ b/res/layout/floating_app_pair_view.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.android.quickstep.views.FloatingAppPairView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> +</com.android.quickstep.views.FloatingAppPairView>
\ No newline at end of file diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 20e708982a..8d84c90f40 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -164,7 +164,19 @@ <!-- numFolderRows & numFolderColumns defaults to numRows & numColumns, if not specified --> <attr name="numFolderRows" format="integer" /> + <!-- defaults to numFolderRows, if not specified --> + <attr name="numFolderRowsLandscape" format="integer" /> + <!-- defaults to numFolderRows, if not specified --> + <attr name="numFolderRowsTwoPanelLandscape" format="integer" /> + <!-- defaults to numFolderRows, if not specified --> + <attr name="numFolderRowsTwoPanelPortrait" format="integer" /> <attr name="numFolderColumns" format="integer" /> + <!-- defaults to numFolderColumns, if not specified --> + <attr name="numFolderColumnsLandscape" format="integer" /> + <!-- defaults to numFolderColumns, if not specified --> + <attr name="numFolderColumnsTwoPanelLandscape" format="integer" /> + <!-- defaults to numFolderColumns, if not specified --> + <attr name="numFolderColumnsTwoPanelPortrait" format="integer" /> <!-- Support attributes in FolderStyle --> <attr name="folderStyle" format="reference" /> diff --git a/res/values/config.xml b/res/values/config.xml index 154312ad4b..2980635634 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -86,6 +86,7 @@ <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> + <string name="launcher_restore_event_logger_class" translatable="false"></string> <!-- View ID to use for QSB widget --> <item type="id" name="qsb_widget" /> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 025003bbf4..0a57127e32 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -125,6 +125,9 @@ <dimen name="all_apps_tip_bottom_margin">8dp</dimen> <dimen name="all_apps_height_extra">6dp</dimen> <dimen name="all_apps_paged_view_top_padding">40dp</dimen> + <dimen name="all_apps_recycler_view_decorator_padding">1dp</dimen> + <dimen name="all_apps_recycler_view_decorator_group_radius">28dp</dimen> + <dimen name="all_apps_recycler_view_decorator_result_radius">4dp</dimen> <dimen name="all_apps_icon_drawable_padding">8dp</dimen> <dimen name="all_apps_predicted_icon_vertical_padding">8dp</dimen> @@ -176,6 +179,7 @@ <dimen name="widget_tabs_horizontal_padding">16dp</dimen> <dimen name="widget_apps_tabs_vertical_padding">6dp</dimen> <dimen name="widget_picker_landscape_tablet_left_right_margin">117dp</dimen> + <dimen name="widget_picker_two_panels_left_right_margin">0dp</dimen> <dimen name="recommended_widgets_table_vertical_padding">8dp</dimen> @@ -363,6 +367,7 @@ <!-- Taskbar related (placeholders to compile in Launcher3 without Quickstep) --> <dimen name="taskbar_size">0dp</dimen> + <dimen name="taskbar_phone_size">@*android:dimen/navigation_bar_frame_height</dimen> <dimen name="taskbar_stashed_size">0dp</dimen> <dimen name="qsb_widget_height">0dp</dimen> <dimen name="qsb_shadow_height">0dp</dimen> diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index b845c88d23..f72c55615c 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -104,6 +104,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public static final int TYPE_ADD_TO_HOME_CONFIRMATION = 1 << 19; public static final int TYPE_TASKBAR_OVERLAY_PROXY = 1 << 20; public static final int TYPE_TASKBAR_PINNING_POPUP = 1 << 21; + public static final int TYPE_PIN_IME_POPUP = 1 << 22; public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET @@ -112,17 +113,18 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch | TYPE_ICON_SURFACE | TYPE_DRAG_DROP_POPUP | TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP | TYPE_WIDGETS_EDUCATION_DIALOG | TYPE_TASKBAR_EDUCATION_DIALOG | TYPE_TASKBAR_ALL_APPS | TYPE_OPTIONS_POPUP_DIALOG | TYPE_ADD_TO_HOME_CONFIRMATION - | TYPE_TASKBAR_OVERLAY_PROXY | TYPE_TASKBAR_PINNING_POPUP; + | TYPE_TASKBAR_OVERLAY_PROXY | TYPE_TASKBAR_PINNING_POPUP | TYPE_PIN_IME_POPUP; // Type of popups which should be kept open during launcher rebind public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE | TYPE_WIDGETS_EDUCATION_DIALOG | TYPE_TASKBAR_EDUCATION_DIALOG | TYPE_TASKBAR_ALL_APPS | TYPE_OPTIONS_POPUP_DIALOG - | TYPE_TASKBAR_OVERLAY_PROXY; + | TYPE_TASKBAR_OVERLAY_PROXY | TYPE_PIN_IME_POPUP; + /** Type of popups that should get exclusive accessibility focus. */ public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER - & ~TYPE_ALL_APPS_EDU; + & ~TYPE_ALL_APPS_EDU & ~TYPE_TASKBAR_ALL_APPS & ~TYPE_PIN_IME_POPUP; // These view all have particular operation associated with swipe down interaction. public static final int TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW = TYPE_WIDGETS_BOTTOM_SHEET | @@ -133,7 +135,12 @@ 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; + // Floating views that a TouchController should not try to intercept touches from. + public static final int TYPE_TOUCH_CONTROLLER_NO_INTERCEPT = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE + & ~TYPE_LISTENER & ~TYPE_TASKBAR_OVERLAYS; + + public static final int TYPE_ALL_EXCEPT_ON_BOARD_POPUP = TYPE_ALL & ~TYPE_ON_BOARD_POPUP + & ~TYPE_PIN_IME_POPUP; protected boolean mIsOpen; diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java index 641fd834e2..429978eeb4 100644 --- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java +++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java @@ -9,9 +9,13 @@ import android.content.Context; import android.content.Intent; import android.util.Log; +import com.android.launcher3.logging.FileLog; +import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.util.IntArray; import com.android.launcher3.widget.LauncherWidgetHolder; +import java.util.Arrays; + public class AppWidgetsRestoredReceiver extends BroadcastReceiver { private static final String TAG = "AppWidgetsRestoredReceiver"; @@ -20,8 +24,11 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { public void onReceive(final Context context, Intent intent) { if (AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED.equals(intent.getAction())) { int hostId = intent.getIntExtra(AppWidgetManager.EXTRA_HOST_ID, 0); - Log.d(TAG, "Widget ID map received for host:" + hostId); + Log.d(TAG, "onReceive: Widget ID map received for host:" + hostId); if (hostId != LauncherWidgetHolder.APPWIDGET_HOST_ID) { + Log.w(TAG, "onReceive: hostId does not match Launcher." + + " Expected: " + LauncherWidgetHolder.APPWIDGET_HOST_ID + + ", Actual: " + hostId); return; } @@ -31,8 +38,18 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { LauncherPrefs.get(context).putSync( OLD_APP_WIDGET_IDS.to(IntArray.wrap(oldIds).toConcatString()), APP_WIDGET_IDS.to(IntArray.wrap(newIds).toConcatString())); + FileLog.d(TAG, "onReceive: Valid Widget IDs received." + + " old IDs=" + Arrays.toString(oldIds) + + ", new IDs=" + Arrays.toString(newIds)); + if (!RestoreDbTask.isPending(context)) { + FileLog.w(TAG, "onReceive: Restored App Widget Ids received but Launcher" + + " restore is not pending. New widget Ids might not get restored."); + } } else { - Log.e(TAG, "Invalid host restored received"); + Log.e(TAG, "onReceive: Invalid widget ids received for Launcher" + + ", skipping restore of widget ids." + + " newIds=" + Arrays.toString(newIds) + + ", oldIds=" + Arrays.toString(oldIds)); } } } diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index e2e528c968..91da7e6404 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -175,6 +175,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, @ViewDebug.ExportedProperty(category = "launcher") private DotInfo mDotInfo; private DotRenderer mDotRenderer; + private Locale mCurrentLocale; @ViewDebug.ExportedProperty(category = "launcher", deepExport = true) protected DotRenderer.DrawParams mDotParams; private Animator mDotScaleAnim; @@ -250,6 +251,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, mDotParams = new DotRenderer.DrawParams(); + mCurrentLocale = context.getResources().getConfiguration().locale; setEllipsize(TruncateAt.END); setAccessibilityDelegate(mActivity.getAccessibilityDelegate()); setTextAlpha(1f); @@ -411,10 +413,12 @@ 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.enableTwolineAllapps()) - && (mDisplay == DISPLAY_ALL_APPS || mDisplay == DISPLAY_PREDICTION_ROW)) - || (FeatureFlags.ENABLE_TWOLINE_DEVICESEARCH.get() - && mDisplay == DISPLAY_SEARCH_RESULT); + return (FeatureFlags.enableTwolineAllapps() && isCurrentLanguageEnglish()) + && (mDisplay == DISPLAY_ALL_APPS || mDisplay == DISPLAY_PREDICTION_ROW); + } + + protected boolean isCurrentLanguageEnglish() { + return mCurrentLocale.equals(Locale.US); } @UiThread diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 7d15f7b7c0..5443ff992d 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -70,6 +70,7 @@ import com.android.launcher3.celllayout.CellPosMapper.CellPos; import com.android.launcher3.celllayout.DelegatedCellDrawing; import com.android.launcher3.celllayout.ItemConfiguration; import com.android.launcher3.celllayout.ReorderAlgorithm; +import com.android.launcher3.celllayout.ReorderParameters; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.folder.PreviewBackground; @@ -1748,8 +1749,11 @@ public class CellLayout extends ViewGroup { protected ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX, ItemConfiguration solution) { - return createReorderAlgorithm().findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, - spanX, spanY, direction, dragView, decX, solution); + ItemConfiguration configuration = new ItemConfiguration(); + copyCurrentStateToSolution(configuration); + ReorderParameters parameters = new ReorderParameters(pixelX, pixelY, spanX, spanY, minSpanX, + minSpanY, dragView, configuration); + return createReorderAlgorithm().findReorderSolution(parameters, decX); } public void copyCurrentStateToSolution(ItemConfiguration solution) { @@ -1779,8 +1783,12 @@ public class CellLayout extends ViewGroup { */ public ItemConfiguration calculateReorder(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY, View dragView) { - return createReorderAlgorithm().calculateReorder(pixelX, pixelY, minSpanX, minSpanY, - spanX, spanY, dragView); + ItemConfiguration configuration = new ItemConfiguration(); + copyCurrentStateToSolution(configuration); + return createReorderAlgorithm().calculateReorder( + new ReorderParameters(pixelX, pixelY, spanX, spanY, minSpanX, minSpanY, dragView, + configuration) + ); } int[] performReorder(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY, diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 338a9ea724..1ca7da9d44 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -182,6 +182,8 @@ public class DeviceProfile { public int cellYPaddingPx = -1; // Folder + public final int numFolderRows; + public final int numFolderColumns; public final float folderLabelTextScale; public int folderLabelTextSizePx; public int folderFooterHeightPx; @@ -439,6 +441,8 @@ public class DeviceProfile { } folderLabelTextScale = res.getFloat(R.dimen.folder_label_text_scale); + numFolderRows = inv.numFolderRows[mTypeIndex]; + numFolderColumns = inv.numFolderColumns[mTypeIndex]; if (mIsScalableGrid && inv.folderStyle != INVALID_RESOURCE_HANDLE) { TypedArray folderStyle = context.obtainStyledAttributes(inv.folderStyle, @@ -645,11 +649,11 @@ public class DeviceProfile { isTwoPanels ? inv.folderSpecsTwoPanelId : inv.folderSpecsId), ResponsiveSpecType.Folder); mResponsiveFolderWidthSpec = folderSpecs.getCalculatedSpec(responsiveAspectRatio, - DimensionType.WIDTH, inv.numFolderColumns, + DimensionType.WIDTH, numFolderColumns, mResponsiveWorkspaceWidthSpec.getAvailableSpace(), mResponsiveWorkspaceWidthSpec); mResponsiveFolderHeightSpec = folderSpecs.getCalculatedSpec(responsiveAspectRatio, - DimensionType.HEIGHT, inv.numFolderRows, + DimensionType.HEIGHT, numFolderRows, mResponsiveWorkspaceHeightSpec.getAvailableSpace(), mResponsiveWorkspaceHeightSpec); @@ -1406,16 +1410,16 @@ public class DeviceProfile { Point totalWorkspacePadding = getTotalWorkspacePadding(); // Check if the folder fit within the available height. - float contentUsedHeight = folderCellHeightPx * inv.numFolderRows - + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacePx.y) + float contentUsedHeight = folderCellHeightPx * numFolderRows + + ((numFolderRows - 1) * folderCellLayoutBorderSpacePx.y) + folderFooterHeightPx + folderContentPaddingTop; int contentMaxHeight = availableHeightPx - totalWorkspacePadding.y; float scaleY = contentMaxHeight / contentUsedHeight; // Check if the folder fit within the available width. - float contentUsedWidth = folderCellWidthPx * inv.numFolderColumns - + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacePx.x) + float contentUsedWidth = folderCellWidthPx * numFolderColumns + + ((numFolderColumns - 1) * folderCellLayoutBorderSpacePx.x) + folderContentPaddingLeftRight * 2; int contentMaxWidth = availableWidthPx - totalWorkspacePadding.x; float scaleX = contentMaxWidth / contentUsedWidth; @@ -1451,7 +1455,7 @@ public class DeviceProfile { } // Recalculating padding and cell height - folderChildDrawablePaddingPx = getNormalizedFolderChildDrawablePaddingPx(textHeight); + folderChildDrawablePaddingPx = mResponsiveWorkspaceCellSpec.getIconDrawablePadding(); CellContentDimensions cellContentDimensions = new CellContentDimensions( folderChildIconSizePx, @@ -2053,8 +2057,8 @@ public class DeviceProfile { writer.println(prefix + pxToDpStr("iconTextSizePx", iconTextSizePx)); writer.println(prefix + pxToDpStr("iconDrawablePaddingPx", iconDrawablePaddingPx)); - writer.println(prefix + "\tinv.numFolderRows: " + inv.numFolderRows); - writer.println(prefix + "\tinv.numFolderColumns: " + inv.numFolderColumns); + writer.println(prefix + "\tnumFolderRows: " + numFolderRows); + writer.println(prefix + "\tnumFolderColumns: " + numFolderColumns); writer.println(prefix + pxToDpStr("folderCellWidthPx", folderCellWidthPx)); writer.println(prefix + pxToDpStr("folderCellHeightPx", folderCellHeightPx)); writer.println(prefix + pxToDpStr("folderChildIconSizePx", folderChildIconSizePx)); diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 567d0c503f..5721ed3493 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -122,8 +122,8 @@ public class InvariantDeviceProfile { /** * Number of icons per row and column in the folder. */ - public int numFolderRows; - public int numFolderColumns; + public int[] numFolderRows; + public int[] numFolderColumns; public float[] iconSize; public float[] iconTextSize; public int iconBitmapSize; @@ -298,11 +298,15 @@ 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; + FileLog.d(TAG, "Reinitializing grid after restore." + + " currentGridName=" + currentGridName + + ", currentDbFile=" + currentDbFile + + ", newGridName=" + newGridName + + ", newDbFile=" + newDbFile); if (!newDbFile.equals(currentDbFile)) { FileLog.d(TAG, "Restored grid is disabled : " + currentGridName + ", migrating to: " + newGridName @@ -810,8 +814,8 @@ public class InvariantDeviceProfile { public final int numSearchContainerColumns; public final int deviceCategory; - private final int numFolderRows; - private final int numFolderColumns; + private final int[] numFolderRows = new int[COUNT_SIZES]; + private final int[] numFolderColumns = new int[COUNT_SIZES]; private final @StyleRes int folderStyle; private final @StyleRes int cellStyle; @@ -888,11 +892,39 @@ public class InvariantDeviceProfile { a.getResourceId(R.styleable.GridDisplayOption_inlineNavButtonsEndSpacing, R.dimen.taskbar_button_margin_default); - numFolderRows = a.getInt( + numFolderRows[INDEX_DEFAULT] = a.getInt( R.styleable.GridDisplayOption_numFolderRows, numRows); - numFolderColumns = a.getInt( + numFolderColumns[INDEX_DEFAULT] = a.getInt( R.styleable.GridDisplayOption_numFolderColumns, numColumns); + if (FeatureFlags.enableResponsiveWorkspace()) { + numFolderRows[INDEX_LANDSCAPE] = a.getInt( + R.styleable.GridDisplayOption_numFolderRowsLandscape, + numFolderRows[INDEX_DEFAULT]); + numFolderColumns[INDEX_LANDSCAPE] = a.getInt( + R.styleable.GridDisplayOption_numFolderColumnsLandscape, + numFolderColumns[INDEX_DEFAULT]); + numFolderRows[INDEX_TWO_PANEL_PORTRAIT] = a.getInt( + R.styleable.GridDisplayOption_numFolderRowsTwoPanelPortrait, + numFolderRows[INDEX_DEFAULT]); + numFolderColumns[INDEX_TWO_PANEL_PORTRAIT] = a.getInt( + R.styleable.GridDisplayOption_numFolderColumnsTwoPanelPortrait, + numFolderColumns[INDEX_DEFAULT]); + numFolderRows[INDEX_TWO_PANEL_LANDSCAPE] = a.getInt( + R.styleable.GridDisplayOption_numFolderRowsTwoPanelLandscape, + numFolderRows[INDEX_DEFAULT]); + numFolderColumns[INDEX_TWO_PANEL_LANDSCAPE] = a.getInt( + R.styleable.GridDisplayOption_numFolderColumnsTwoPanelLandscape, + numFolderColumns[INDEX_DEFAULT]); + } else { + numFolderRows[INDEX_LANDSCAPE] = numFolderRows[INDEX_DEFAULT]; + numFolderColumns[INDEX_LANDSCAPE] = numFolderColumns[INDEX_DEFAULT]; + numFolderRows[INDEX_TWO_PANEL_PORTRAIT] = numFolderRows[INDEX_DEFAULT]; + numFolderColumns[INDEX_TWO_PANEL_PORTRAIT] = numFolderColumns[INDEX_DEFAULT]; + numFolderRows[INDEX_TWO_PANEL_LANDSCAPE] = numFolderRows[INDEX_DEFAULT]; + numFolderColumns[INDEX_TWO_PANEL_LANDSCAPE] = numFolderColumns[INDEX_DEFAULT]; + } + folderStyle = a.getResourceId(R.styleable.GridDisplayOption_folderStyle, INVALID_RESOURCE_HANDLE); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 0278e4f5c6..e41a8a544c 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -22,7 +22,6 @@ 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; import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; @@ -81,7 +80,6 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE; import static com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION; import static com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_VIEW_INFLATION; -import static com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC; import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType.COLD; import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType.COLD_DEVICE_REBOOTING; import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType.WARM; @@ -140,7 +138,6 @@ import android.view.Menu; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnPreDrawListener; import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityEvent; @@ -160,7 +157,6 @@ import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.allapps.ActivityAllAppsContainerView; import com.android.launcher3.allapps.AllAppsRecyclerView; -import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimationSuccessListener; @@ -190,6 +186,7 @@ import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.InstanceIdSequence; import com.android.launcher3.logging.StartupLatencyLogger; import com.android.launcher3.logging.StatsLogManager; +import com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent; import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.ItemInstallQueue; import com.android.launcher3.model.ModelWriter; @@ -351,14 +348,9 @@ public class Launcher extends StatefulActivity<LauncherState> // UI and state for the overview panel private View mOverviewPanel; - @Thunk - boolean mWorkspaceLoading = true; - // Used to notify when an activity launch has been deferred because launcher is not yet resumed // TODO: See if we can remove this later private Runnable mOnDeferredActivityLaunchCallback; - - private ViewOnDrawExecutor mPendingExecutor; private OnPreDrawListener mOnInitialBindListener; private LauncherModel mModel; @@ -797,7 +789,7 @@ public class Launcher extends StatefulActivity<LauncherState> if (info.container >= 0) { View folderIcon = getWorkspace().getHomescreenIconByItemId(info.container); if (folderIcon instanceof FolderIcon && folderIcon.getTag() instanceof FolderInfo) { - if (new FolderGridOrganizer(getDeviceProfile().inv) + if (new FolderGridOrganizer(getDeviceProfile()) .setFolderInfo((FolderInfo) folderIcon.getTag()) .isItemInPreview(info.rank)) { folderIcon.invalidate(); @@ -853,6 +845,17 @@ public class Launcher extends StatefulActivity<LauncherState> return screenId; } + /** + * Process any pending activity result if it was put on hold for any reason like item binding. + */ + public void processActivityResult() { + if (mPendingActivityResult != null) { + handleActivityResult(mPendingActivityResult.requestCode, + mPendingActivityResult.resultCode, mPendingActivityResult.data); + mPendingActivityResult = null; + } + } + private void handleActivityResult( final int requestCode, final int resultCode, final Intent data) { if (isWorkspaceLoading()) { @@ -1075,7 +1078,7 @@ public class Launcher extends StatefulActivity<LauncherState> } private void logStopAndResume(boolean isResume) { - if (mPendingExecutor != null) return; + if (mModelCallbacks.getPendingExecutor() != null) return; int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage(); int statsLogOrdinal = mStateManager.getState().statsLogOrdinal; @@ -1715,9 +1718,13 @@ public class Launcher extends StatefulActivity<LauncherState> mAppWidgetHolder.destroy(); TextKeyListener.getInstance().release(); - clearPendingBinds(); + mModelCallbacks.clearPendingBinds(); LauncherAppState.getIDP(this).removeOnChangeListener(this); - + // if Launcher activity is recreated, {@link Window} including {@link ViewTreeObserver} + // could be preserved in {@link ActivityThread#scheduleRelaunchActivity(IBinder)} if the + // previous activity has not stopped, which could happen when wallpaper detects a color + // changes while launcher is still loading. + getRootView().getViewTreeObserver().removeOnPreDrawListener(mOnInitialBindListener); mOverlayManager.onActivityDestroyed(); } @@ -2077,48 +2084,9 @@ public class Launcher extends StatefulActivity<LauncherState> return mModelCallbacks.getPagesToBindSynchronously(orderedScreenIds); } - /** - * Clear any pending bind callbacks. This is called when is loader is planning to - * perform a full rebind from scratch. - */ - @Override - public void clearPendingBinds() { - if (mPendingExecutor != null) { - mPendingExecutor.cancel(); - mPendingExecutor = null; - - // We might have set this flag previously and forgot to clear it. - mAppsView.getAppsStore() - .disableDeferUpdatesSilently(AllAppsStore.DEFER_UPDATES_NEXT_DRAW); - } - } - - /** - * Refreshes the shortcuts shown on the workspace. - * <p> - * Implementation of the method from LauncherModel.Callbacks. - */ @Override public void 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. - AbstractFloatingView.closeOpenViews(this, true, TYPE_ALL & ~TYPE_REBIND_SAFE); - - setWorkspaceLoading(true); - - // Clear the workspace because it's going to be rebound - mDragController.cancelDrag(); - - mWorkspace.clearDropTargets(); - mWorkspace.removeAllWorkspaceScreens(); - mAppWidgetHolder.clearViews(); - - if (mHotseat != null) { - mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout()); - } - TraceHelper.INSTANCE.endSection(); + mModelCallbacks.startBinding(); } @Override @@ -2500,44 +2468,25 @@ public class Launcher extends StatefulActivity<LauncherState> } public void clearPendingExecutor(ViewOnDrawExecutor executor) { - if (mPendingExecutor == executor) { - mPendingExecutor = null; + if (mModelCallbacks.getPendingExecutor() == executor) { + mModelCallbacks.setPendingExecutor(null); } } - @Override + /** + * Call back when ModelCallbacks finish binding the Launcher data. + */ @TargetApi(Build.VERSION_CODES.S) - public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks, - int workspaceItemCount, boolean isBindSync) { - mModelCallbacks.setSynchronouslyBoundPages(boundPages); - mModelCallbacks.setPagesToBindSynchronously(new IntSet()); - - clearPendingBinds(); - ViewOnDrawExecutor executor = new ViewOnDrawExecutor(pendingTasks); - mPendingExecutor = executor; - if (!isInState(ALL_APPS)) { - mAppsView.getAppsStore().enableDeferUpdates(AllAppsStore.DEFER_UPDATES_NEXT_DRAW); - pendingTasks.add(() -> mAppsView.getAppsStore().disableDeferUpdates( - AllAppsStore.DEFER_UPDATES_NEXT_DRAW)); - } - + public void bindComplete(int workspaceItemCount, boolean isBindSync) { if (mOnInitialBindListener != null) { getRootView().getViewTreeObserver().removeOnPreDrawListener(mOnInitialBindListener); mOnInitialBindListener = null; } - - executor.onLoadAnimationCompleted(); - executor.attachTo(this); - if (Utilities.ATLEAST_S) { - Trace.endAsyncSection(DISPLAY_WORKSPACE_TRACE_METHOD_NAME, - DISPLAY_WORKSPACE_TRACE_COOKIE); - } if (!isBindSync) { mStartupLatencyLogger .logCardinality(workspaceItemCount) - .logEnd(LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC); + .logEnd(LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC); } - MAIN_EXECUTOR.getHandler().postAtFrontOfQueue(() -> { mStartupLatencyLogger .logEnd(LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION) @@ -2548,15 +2497,13 @@ public class Launcher extends StatefulActivity<LauncherState> COLD_STARTUP_TRACE_COOKIE); } }); - getRootView().getViewTreeObserver().addOnDrawListener( - new ViewTreeObserver.OnDrawListener() { - @Override - public void onDraw() { - MAIN_EXECUTOR.getHandler().postAtFrontOfQueue( - () -> getRootView().getViewTreeObserver() - .removeOnDrawListener(this)); - } - }); + } + + @Override + public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks, + int workspaceItemCount, boolean isBindSync) { + mModelCallbacks.onInitialBindComplete(boundPages, pendingTasks, workspaceItemCount, + isBindSync); } /** @@ -2565,34 +2512,7 @@ public class Launcher extends StatefulActivity<LauncherState> * Implementation of the method from LauncherModel.Callbacks. */ public void finishBindingItems(IntSet pagesBoundFirst) { - TraceHelper.INSTANCE.beginSection("finishBindingItems"); - mWorkspace.restoreInstanceStateForRemainingPages(); - - setWorkspaceLoading(false); - - if (mPendingActivityResult != null) { - handleActivityResult(mPendingActivityResult.requestCode, - mPendingActivityResult.resultCode, mPendingActivityResult.data); - mPendingActivityResult = null; - } - - int currentPage = pagesBoundFirst != null && !pagesBoundFirst.isEmpty() - ? mWorkspace.getPageIndexForScreenId(pagesBoundFirst.getArray().get(0)) - : PagedView.INVALID_PAGE; - // When undoing the removal of the last item on a page, return to that page. - // Since we are just resetting the current page without user interaction, - // override the previous page so we don't log the page switch. - mWorkspace.setCurrentPage(currentPage, currentPage /* overridePrevPage */); - mModelCallbacks.setPagesToBindSynchronously(new IntSet()); - - // Cache one page worth of icons - getViewCache().setCacheSize(R.layout.folder_application, - mDeviceProfile.inv.numFolderColumns * mDeviceProfile.inv.numFolderRows); - getViewCache().setCacheSize(R.layout.folder_page, 2); - - TraceHelper.INSTANCE.endSection(); - mWorkspace.removeExtraEmptyScreen(/* stripEmptyScreens= */ true); - mWorkspace.mPageIndicator.setAreScreensBinding(false, mDeviceProfile.isTwoPanels); + mModelCallbacks.finishBindingItems(pagesBoundFirst); } private boolean canAnimatePageChange() { @@ -2851,7 +2771,7 @@ public class Launcher extends StatefulActivity<LauncherState> writer.println(prefix + "Misc:"); dumpMisc(prefix + "\t", writer); - writer.println(prefix + "\tmWorkspaceLoading=" + mWorkspaceLoading); + writer.println(prefix + "\tmWorkspaceLoading=" + mModelCallbacks.getWorkspaceLoading()); writer.println(prefix + "\tmPendingRequestArgs=" + mPendingRequestArgs + " mPendingActivityResult=" + mPendingActivityResult); writer.println(prefix + "\tmRotationHelper: " + mRotationHelper); @@ -2985,6 +2905,14 @@ public class Launcher extends StatefulActivity<LauncherState> // Overridden; move this into ActivityContext if necessary for Taskbar } + /** + * Callback for when launcher state transition completes after user swipes to home. + * @param finalState The final state of the transition. + */ + public void onStateTransitionCompletedAfterSwipeToHome(LauncherState finalState) { + // Overridden + } + @Override public void returnToHomescreen() { super.returnToHomescreen(); @@ -3079,21 +3007,17 @@ public class Launcher extends StatefulActivity<LauncherState> // Getters and Setters - private void setWorkspaceLoading(boolean value) { - mWorkspaceLoading = value; - } - public boolean isWorkspaceLocked() { - return mWorkspaceLoading || mPendingRequestArgs != null; + return isWorkspaceLoading() || mPendingRequestArgs != null; } public boolean isWorkspaceLoading() { - return mWorkspaceLoading; + return mModelCallbacks.getWorkspaceLoading(); } @Override public boolean isBindingItems() { - return mWorkspaceLoading; + return isWorkspaceLoading(); } /** @@ -3273,7 +3197,7 @@ public class Launcher extends StatefulActivity<LauncherState> * Handles an app pair launch; overridden in * {@link com.android.launcher3.uioverrides.QuickstepLauncher} */ - public void launchAppPair(WorkspaceItemInfo app1, WorkspaceItemInfo app2) { + public void launchAppPair(AppPairIcon appPairIcon) { // Overridden } diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt index a05b0f5a98..78056e625b 100644 --- a/src/com/android/launcher3/LauncherPrefs.kt +++ b/src/com/android/launcher3/LauncherPrefs.kt @@ -364,6 +364,13 @@ class LauncherPrefs(private val encryptedContext: Context) { EncryptionType.MOVE_TO_DEVICE_PROTECTED ) @JvmField + val PRIVATE_SPACE_APPS = + nonRestorableItem( + "pref_private_space_apps", + 0, + EncryptionType.MOVE_TO_DEVICE_PROTECTED + ) + @JvmField val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false, EncryptionType.MOVE_TO_DEVICE_PROTECTED) @JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "") diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt index bcd30d3d2e..51729992d4 100644 --- a/src/com/android/launcher3/ModelCallbacks.kt +++ b/src/com/android/launcher3/ModelCallbacks.kt @@ -1,7 +1,13 @@ package com.android.launcher3 +import android.annotation.TargetApi +import android.os.Build +import android.os.Trace +import android.view.ViewTreeObserver.OnDrawListener import androidx.annotation.UiThread +import com.android.launcher3.LauncherConstants.TraceEvents import com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID +import com.android.launcher3.allapps.AllAppsStore import com.android.launcher3.config.FeatureFlags import com.android.launcher3.config.FeatureFlags.shouldShowFirstPageWidget import com.android.launcher3.model.BgDataModel @@ -12,12 +18,14 @@ import com.android.launcher3.model.data.LauncherAppWidgetInfo import com.android.launcher3.model.data.WorkspaceItemInfo import com.android.launcher3.popup.PopupContainerWithArrow import com.android.launcher3.util.ComponentKey +import com.android.launcher3.util.Executors import com.android.launcher3.util.IntArray as LIntArray -import com.android.launcher3.util.IntArray import com.android.launcher3.util.IntSet as LIntSet -import com.android.launcher3.util.IntSet import com.android.launcher3.util.PackageUserKey import com.android.launcher3.util.Preconditions +import com.android.launcher3.util.RunnableList +import com.android.launcher3.util.TraceHelper +import com.android.launcher3.util.ViewOnDrawExecutor import com.android.launcher3.widget.PendingAddWidgetInfo import com.android.launcher3.widget.model.WidgetsListBaseEntry import java.util.function.Predicate @@ -27,11 +35,127 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks { var synchronouslyBoundPages = LIntSet() var pagesToBindSynchronously = LIntSet() - var isFirstPagePinnedItemEnabled = + private var isFirstPagePinnedItemEnabled = (BuildConfig.QSB_ON_FIRST_SCREEN && !FeatureFlags.ENABLE_SMARTSPACE_REMOVAL.get()) var stringCache: StringCache? = null + var pendingExecutor: ViewOnDrawExecutor? = null + + var workspaceLoading = true + + /** + * Refreshes the shortcuts shown on the workspace. + * + * Implementation of the method from LauncherModel.Callbacks. + */ + override fun 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. + AbstractFloatingView.closeOpenViews( + launcher, + true, + AbstractFloatingView.TYPE_ALL and AbstractFloatingView.TYPE_REBIND_SAFE.inv() + ) + workspaceLoading = true + + // Clear the workspace because it's going to be rebound + launcher.dragController.cancelDrag() + launcher.workspace.clearDropTargets() + launcher.workspace.removeAllWorkspaceScreens() + launcher.appWidgetHolder.clearViews() + launcher.hotseat?.resetLayout(launcher.deviceProfile.isVerticalBarLayout) + TraceHelper.INSTANCE.endSection() + } + + @TargetApi(Build.VERSION_CODES.S) + override fun onInitialBindComplete( + boundPages: LIntSet, + pendingTasks: RunnableList, + workspaceItemCount: Int, + isBindSync: Boolean + ) { + synchronouslyBoundPages = boundPages + pagesToBindSynchronously = LIntSet() + clearPendingBinds() + val executor = ViewOnDrawExecutor(pendingTasks) + pendingExecutor = executor + if (!launcher.isInState(LauncherState.ALL_APPS)) { + launcher.appsView.appsStore.enableDeferUpdates(AllAppsStore.DEFER_UPDATES_NEXT_DRAW) + pendingTasks.add { + launcher.appsView.appsStore.disableDeferUpdates( + AllAppsStore.DEFER_UPDATES_NEXT_DRAW + ) + } + } + executor.onLoadAnimationCompleted() + executor.attachTo(launcher) + if (Utilities.ATLEAST_S) { + Trace.endAsyncSection( + TraceEvents.DISPLAY_WORKSPACE_TRACE_METHOD_NAME, + TraceEvents.DISPLAY_WORKSPACE_TRACE_COOKIE + ) + } + launcher.bindComplete(workspaceItemCount, isBindSync) + launcher.rootView.viewTreeObserver.addOnDrawListener( + object : OnDrawListener { + override fun onDraw() { + Executors.MAIN_EXECUTOR.handler.postAtFrontOfQueue { + launcher.rootView.getViewTreeObserver().removeOnDrawListener(this) + } + } + } + ) + } + + /** + * Callback saying that there aren't any more items to bind. + * + * Implementation of the method from LauncherModel.Callbacks. + */ + override fun finishBindingItems(pagesBoundFirst: LIntSet?) { + TraceHelper.INSTANCE.beginSection("finishBindingItems") + val deviceProfile = launcher.deviceProfile + launcher.workspace.restoreInstanceStateForRemainingPages() + workspaceLoading = false + launcher.processActivityResult() + val currentPage = + if (pagesBoundFirst != null && !pagesBoundFirst.isEmpty) + launcher.workspace.getPageIndexForScreenId(pagesBoundFirst.array[0]) + else PagedView.INVALID_PAGE + // When undoing the removal of the last item on a page, return to that page. + // Since we are just resetting the current page without user interaction, + // override the previous page so we don't log the page switch. + launcher.workspace.setCurrentPage(currentPage, currentPage /* overridePrevPage */) + pagesToBindSynchronously = LIntSet() + + // Cache one page worth of icons + launcher.viewCache.setCacheSize( + R.layout.folder_application, + deviceProfile.numFolderColumns * deviceProfile.numFolderRows + ) + launcher.viewCache.setCacheSize(R.layout.folder_page, 2) + TraceHelper.INSTANCE.endSection() + launcher.workspace.removeExtraEmptyScreen(/* stripEmptyScreens= */ true) + launcher.workspace.pageIndicator.setAreScreensBinding(false, deviceProfile.isTwoPanels) + } + + /** + * Clear any pending bind callbacks. This is called when is loader is planning to perform a full + * rebind from scratch. + */ + override fun clearPendingBinds() { + pendingExecutor?.cancel() ?: return + pendingExecutor = null + + // We might have set this flag previously and forgot to clear it. + launcher.appsView.appsStore.disableDeferUpdatesSilently( + AllAppsStore.DEFER_UPDATES_NEXT_DRAW + ) + } + override fun preAddApps() { // If there's an undo snackbar, force it to complete to ensure empty screens are removed // before trying to add new items. @@ -119,7 +243,7 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks { val visibleIds = when { !pagesToBindSynchronously.isEmpty -> pagesToBindSynchronously - !launcher.isWorkspaceLoading -> launcher.workspace.currentPageScreenIds + !workspaceLoading -> launcher.workspace.currentPageScreenIds else -> synchronouslyBoundPages } // Launcher IntArray has the same name as Kotlin IntArray @@ -178,7 +302,7 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks { ) } - override fun bindScreens(orderedScreenIds: IntArray) { + override fun bindScreens(orderedScreenIds: LIntArray) { launcher.workspace.pageIndicator.setAreScreensBinding( true, launcher.deviceProfile.isTwoPanels @@ -208,7 +332,7 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks { } override fun bindAppsAdded( - newScreens: IntArray?, + newScreens: LIntArray?, addNotAnimated: java.util.ArrayList<ItemInfo?>?, addAnimated: java.util.ArrayList<ItemInfo?>? ) { @@ -233,7 +357,7 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks { launcher.workspace.removeExtraEmptyScreen(false) } - private fun bindAddScreens(orderedScreenIdsArg: IntArray) { + private fun bindAddScreens(orderedScreenIdsArg: LIntArray) { var orderedScreenIds = orderedScreenIdsArg if (launcher.deviceProfile.isTwoPanels) { if (FeatureFlags.FOLDABLE_SINGLE_PAGE.get()) { @@ -241,7 +365,7 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks { } 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. - val screenIds = IntSet.wrap(orderedScreenIds) + val screenIds = LIntSet.wrap(orderedScreenIds) orderedScreenIds.forEach { screenId: Int -> screenIds.add(launcher.workspace.getScreenPair(screenId)) } @@ -264,8 +388,8 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks { * Remove odd number because they are already included when isTwoPanels and add the pair screen * if not present. */ - private fun filterTwoPanelScreenIds(orderedScreenIds: IntArray): IntArray { - val screenIds = IntSet.wrap(orderedScreenIds) + private fun filterTwoPanelScreenIds(orderedScreenIds: LIntArray): LIntArray { + val screenIds = LIntSet.wrap(orderedScreenIds) orderedScreenIds .filter { screenId -> screenId % 2 == 1 } .forEach { screenId -> diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index b74699a097..e0f6101056 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -79,7 +79,6 @@ import androidx.core.graphics.ColorUtils; import com.android.launcher3.dragndrop.FolderAdaptiveIcon; import com.android.launcher3.graphics.TintedDrawableSpan; -import com.android.launcher3.icons.BaseIconFactory; import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.icons.ShortcutCachingLogic; @@ -91,6 +90,7 @@ import com.android.launcher3.pm.UserCache; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.shortcuts.ShortcutRequest; import com.android.launcher3.testing.shared.ResourceUtils; +import com.android.launcher3.util.FlagOp; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.launcher3.util.Themes; @@ -676,12 +676,11 @@ public final class Utilities { } if (badge == null) { - try (LauncherIcons li = LauncherIcons.obtain(context)) { - badge = BitmapInfo.LOW_RES_INFO.withFlags( - li.getBitmapFlagOp(new BaseIconFactory.IconOptions().setUser( - UserCache.INSTANCE.get(context).getUserInfo(info.user)))) - .getBadgeDrawable(context, useTheme); - } + badge = BitmapInfo.LOW_RES_INFO.withFlags( + UserCache.INSTANCE.get(context) + .getUserInfo(info.user) + .applyBitmapInfoFlags(FlagOp.NO_OP)) + .getBadgeDrawable(context, useTheme); if (badge == null) { badge = new ColorDrawable(Color.TRANSPARENT); } diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index e5a223a9f9..7f1d216c95 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -407,7 +407,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> // If exiting search, revert predictive back scale on all apps mAllAppsTransitionController.animateAllAppsToNoScale(); } - mSearchTransitionController.animateToSearchState(goingToSearch, durationMs, + mSearchTransitionController.animateToState(goingToSearch, durationMs, /* onEndRunnable = */ () -> { mIsSearching = goingToSearch; updateSearchResultsVisibility(); diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index b0f13ef863..36a44cc345 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -36,7 +36,11 @@ import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.util.Log; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Consumer; import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.DeviceProfile; @@ -57,6 +61,7 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { protected static final String TAG = "AllAppsRecyclerView"; private static final boolean DEBUG = false; private static final boolean DEBUG_LATENCY = Utilities.isPropertyEnabled(SEARCH_LOGGING); + private Consumer<View> mChildAttachedConsumer; protected final int mNumAppsPerRow; private final AllAppsFastScrollHelper mFastScrollHelper; @@ -282,6 +287,22 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { } } + /** + * This will be called just before a new child is attached to the window. Passing in null will + * remove the consumer. + */ + protected void setChildAttachedConsumer(@Nullable Consumer<View> childAttachedConsumer) { + mChildAttachedConsumer = childAttachedConsumer; + } + + @Override + public void onChildAttachedToWindow(@NonNull View child) { + if (mChildAttachedConsumer != null) { + mChildAttachedConsumer.accept(child); + } + super.onChildAttachedToWindow(child); + } + @Override public int getScrollBarTop() { return ActivityContext.lookupContext(getContext()).getAppsView().isSearchSupported() diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 328516e7e0..17827918b8 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -15,6 +15,10 @@ */ package com.android.launcher3.allapps; +import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_LEFT; +import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_RIGHT; +import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING; + import android.content.Context; import androidx.annotation.Nullable; @@ -318,6 +322,10 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement case PrivateProfileManager.STATE_ENABLED: // Add PS Apps only in Enabled State. addAppsWithSections(mPrivateApps, position); + if (mActivityContext.getAppsView() != null) { + mActivityContext.getAppsView().getActiveRecyclerView() + .scrollToBottomWithMotion(); + } break; } } @@ -325,8 +333,34 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement private void addAppsWithSections(List<AppInfo> appList, int startPosition) { String lastSectionName = null; + boolean hasPrivateApps = false; + if (mPrivateProviderManager != null) { + hasPrivateApps = appList.stream(). + allMatch(mPrivateProviderManager.getItemInfoMatcher()); + } + int privateAppCount = 0; + int numberOfColumns = mActivityContext.getDeviceProfile().numShownAllAppsColumns; + int numberOfAppRows = (int) Math.ceil((double) appList.size() / numberOfColumns); for (AppInfo info : appList) { - mAdapterItems.add(AdapterItem.asApp(info)); + // Apply decorator to private apps. + if (hasPrivateApps) { + int roundRegion = ROUND_NOTHING; + if ((privateAppCount / numberOfColumns) == numberOfAppRows - 1) { + if ((privateAppCount % numberOfColumns) == 0) { + // App is the first column + roundRegion = ROUND_BOTTOM_LEFT; + } else if ((privateAppCount % numberOfColumns) == numberOfColumns-1) { + roundRegion = ROUND_BOTTOM_RIGHT; + } + } + mAdapterItems.add(AdapterItem.asAppWithDecorationInfo(info, + new SectionDecorationInfo(mActivityContext.getApplicationContext(), + roundRegion, + true /* decorateTogether */))); + privateAppCount += 1; + } else { + mAdapterItems.add(AdapterItem.asApp(info)); + } String sectionName = info.sectionName; // Create a new section if the section names do not match diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java index 5e26ea5a9a..5eeb259fe5 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java @@ -15,6 +15,12 @@ */ package com.android.launcher3.allapps; +import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_LEFT; +import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_RIGHT; +import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_TOP_LEFT; +import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_TOP_RIGHT; +import static com.android.launcher3.allapps.UserProfileManager.STATE_DISABLED; + import android.content.Context; import android.view.LayoutInflater; import android.view.View; @@ -25,6 +31,7 @@ import android.view.ViewGroup; import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.BubbleTextView; @@ -92,7 +99,8 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex public int rowAppIndex; // The associated ItemInfoWithIcon for the item public AppInfo itemInfo = null; - + // Private App Decorator + public SectionDecorationInfo decorationInfo = null; public AdapterItem(int viewType) { this.viewType = viewType; } @@ -106,6 +114,13 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex return item; } + public static AdapterItem asAppWithDecorationInfo(AppInfo appInfo, + SectionDecorationInfo decorationInfo) { + AdapterItem item = asApp(appInfo); + item.decorationInfo = decorationInfo; + return item; + } + protected boolean isCountedForAccessibility() { return viewType == VIEW_TYPE_ICON; } @@ -125,9 +140,17 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex return itemInfo == null && other.itemInfo == null; } - /** Sets the alpha of the decorator for this item. Returns true if successful. */ - public boolean setDecorationFillAlpha(int alpha) { - return false; + @Nullable + public SectionDecorationInfo getDecorationInfo() { + return decorationInfo; + } + + /** Sets the alpha of the decorator for this item. */ + protected void setDecorationFillAlpha(int alpha) { + if (decorationInfo == null || decorationInfo.getDecorationHandler() == null) { + return; + } + decorationInfo.getDecorationHandler().setFillAlpha(alpha); } } @@ -249,6 +272,15 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex assert mPrivateSpaceHeaderViewController != null; assert psHeaderLayout != null; mPrivateSpaceHeaderViewController.addPrivateSpaceHeaderViewElements(psHeaderLayout); + AdapterItem adapterItem = mApps.getAdapterItems().get(position); + int roundRegions = ROUND_TOP_LEFT | ROUND_TOP_RIGHT; + if (mPrivateSpaceHeaderViewController.getPrivateProfileManager().getCurrentState() + == STATE_DISABLED) { + roundRegions |= (ROUND_BOTTOM_LEFT | ROUND_BOTTOM_RIGHT); + } + adapterItem.decorationInfo = + new SectionDecorationInfo(mActivityContext, roundRegions, + false /* decorateTogether */); break; case VIEW_TYPE_ALL_APPS_DIVIDER: case VIEW_TYPE_WORK_DISABLED_CARD: diff --git a/src/com/android/launcher3/allapps/PrivateAppsSectionDecorator.java b/src/com/android/launcher3/allapps/PrivateAppsSectionDecorator.java index f4ed754d7d..8712b8468f 100644 --- a/src/com/android/launcher3/allapps/PrivateAppsSectionDecorator.java +++ b/src/com/android/launcher3/allapps/PrivateAppsSectionDecorator.java @@ -16,97 +16,55 @@ package com.android.launcher3.allapps; -import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_ICON; -import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER; - -import android.content.Context; import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.RectF; import android.view.View; -import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; -import com.android.launcher3.R; -import com.android.launcher3.pm.UserCache; -import com.android.launcher3.views.ActivityContext; +import java.util.HashMap; /** * Decorator which changes the background color for Private Space Icon Rows in AllAppsContainer. */ public class PrivateAppsSectionDecorator extends RecyclerView.ItemDecoration { - private final Path mTmpPath = new Path(); - private final RectF mTmpRect = new RectF(); - private final Context mContext; + private static final String PRIVATE_APP_SECTION = "private_apps"; private final AlphabeticalAppsList<?> mAppsList; - private final UserCache mUserCache; - private final Paint mPaint; - private final int mCornerRadius; - public PrivateAppsSectionDecorator(Context context, AlphabeticalAppsList<?> appsList) { - mContext = context; + public PrivateAppsSectionDecorator(AlphabeticalAppsList<?> appsList) { mAppsList = appsList; - mUserCache = UserCache.getInstance(context); - mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mPaint.setColor(ContextCompat.getColor(context, - R.color.material_color_surface_container_high)); - mCornerRadius = context.getResources().getDimensionPixelSize( - R.dimen.ps_container_corner_radius); } /** Decorates Private Space Header and Icon Rows to give the shape of a container. */ @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { - mTmpPath.reset(); - mTmpRect.setEmpty(); - int numCol = ActivityContext.lookupContext(mContext).getDeviceProfile() - .numShownAllAppsColumns; + HashMap<String, SectionDecorationHandler.UnionDecorationHandler> deferredDecorations = + new HashMap<>(); for (int i = 0; i < parent.getChildCount(); i++) { View view = parent.getChildAt(i); int position = parent.getChildAdapterPosition(view); BaseAllAppsAdapter.AdapterItem adapterItem = mAppsList.getAdapterItems().get(position); - // Rectangle that covers the bottom half of the PS Header View when Space is unlocked. - if (adapterItem.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER) { - // We flatten the bottom corners of the rectangle, so that it merges with - // the private space app row decorator. - mTmpRect.set( - view.getLeft(), - view.getTop() + (float) (view.getBottom() - view.getTop()) / 2, - view.getRight(), - view.getBottom()); - mTmpPath.addRect(mTmpRect, Path.Direction.CW); - c.drawPath(mTmpPath, mPaint); - } else if (adapterItem.viewType == VIEW_TYPE_ICON - && mUserCache.getUserInfo(adapterItem.itemInfo.user).isPrivate() - // No decoration for any private space app icon other than those at first row. - && adapterItem.rowAppIndex == 0) { - c.drawPath(getPrivateAppRowPath(parent, view, position, numCol), mPaint); + SectionDecorationInfo info = adapterItem.decorationInfo; + if (info == null) { + continue; + } + SectionDecorationHandler decorationHandler = info.getDecorationHandler(); + if (info.shouldDecorateItemsTogether()) { + SectionDecorationHandler.UnionDecorationHandler unionHandler = + deferredDecorations.getOrDefault( + PRIVATE_APP_SECTION, + new SectionDecorationHandler.UnionDecorationHandler( + decorationHandler, parent.getPaddingLeft(), + parent.getPaddingRight())); + unionHandler.addChild(decorationHandler, view, true /* applyBackground */); + deferredDecorations.put(PRIVATE_APP_SECTION, unionHandler); + } else { + decorationHandler.onFocusDraw(c, view); } } - } - - /** Returns the path to be decorated for Private Space App Row */ - private Path getPrivateAppRowPath(RecyclerView parent, View iconView, int adapterPosition, - int numCol) { - // We always decorate the entire app row here. - // As the iconView just represents the first icon of the row, we get the right margin of - // our decorator using the parent view. - mTmpRect.set(iconView.getLeft(), - iconView.getTop(), - parent.getRight() - parent.getPaddingRight(), - iconView.getBottom()); - // Decorates last app row with rounded bottom corners. - if (adapterPosition + numCol >= mAppsList.getAdapterItems().size()) { - float[] mCornersBot = new float[]{0, 0, 0, 0, mCornerRadius, mCornerRadius, - mCornerRadius, mCornerRadius}; - mTmpPath.addRoundRect(mTmpRect, mCornersBot, Path.Direction.CW); - } else { - // Decorate other rows as a plain rectangle - mTmpPath.addRect(mTmpRect, Path.Direction.CW); + for (SectionDecorationHandler.UnionDecorationHandler decorationHandler + : deferredDecorations.values()) { + decorationHandler.onGroupDecorate(c); } - return mTmpPath; } } diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java index 77eb07e2d3..693681bd1d 100644 --- a/src/com/android/launcher3/allapps/PrivateProfileManager.java +++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java @@ -30,6 +30,7 @@ import android.os.UserManager; import androidx.annotation.VisibleForTesting; +import com.android.launcher3.Flags; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.pm.UserCache; import com.android.launcher3.util.Preconditions; @@ -47,6 +48,7 @@ public class PrivateProfileManager extends UserProfileManager { private static final String SAFETY_CENTER_INTENT = Intent.ACTION_SAFETY_CENTER; private static final String PS_SETTINGS_FRAGMENT_KEY = ":settings:fragment_args_key"; private static final String PS_SETTINGS_FRAGMENT_VALUE = "AndroidPrivateSpace_personal"; + private static final int ANIMATION_DURATION = 2000; private final ActivityAllAppsContainerView<?> mAllApps; private final Predicate<UserHandle> mPrivateProfileMatcher; private PrivateAppsSectionDecorator mPrivateAppsSectionDecorator; @@ -71,13 +73,11 @@ public class PrivateProfileManager extends UserProfileManager { /** Disables quiet mode for Private Space User Profile. */ public void unlockPrivateProfile() { - // TODO (b/302666597): Log this event to WW. enableQuietMode(false); } /** Enables quiet mode for Private Space User Profile. */ public void lockPrivateProfile() { - // TODO (b/302666597): Log this event to WW. enableQuietMode(true); } @@ -98,7 +98,6 @@ public class PrivateProfileManager extends UserProfileManager { /** Opens the Private Space Settings Entry Point. */ public void openPrivateSpaceSettings() { - // TODO (b/302666597): Log this event to WW. Intent psSettingsIntent = new Intent(SAFETY_CENTER_INTENT); psSettingsIntent.putExtra(PS_SETTINGS_FRAGMENT_KEY, PS_SETTINGS_FRAGMENT_VALUE); mAllApps.getContext().startActivity(psSettingsIntent); @@ -128,7 +127,6 @@ public class PrivateProfileManager extends UserProfileManager { // Create a new decorator instance if not already available. if (mPrivateAppsSectionDecorator == null) { mPrivateAppsSectionDecorator = new PrivateAppsSectionDecorator( - mAllApps.mActivityContext, mainAdapterHolder.mAppsList); } for (int i = 0; i < mainAdapterHolder.mRecyclerView.getItemDecorationCount(); i++) { @@ -140,6 +138,13 @@ public class PrivateProfileManager extends UserProfileManager { } // Add Private Space Decorator to the Recycler view. mainAdapterHolder.mRecyclerView.addItemDecoration(mPrivateAppsSectionDecorator); + if (Flags.privateSpaceAnimation() && mAllApps.getActiveRecyclerView() + == mainAdapterHolder.mRecyclerView) { + RecyclerViewAnimationController recyclerViewAnimationController = + new RecyclerViewAnimationController(mAllApps); + recyclerViewAnimationController.animateToState(true /* expand */, + ANIMATION_DURATION, () -> {}); + } } else { // Remove Private Space Decorator from the Recycler view. if (mPrivateAppsSectionDecorator != null) { diff --git a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java index 79e0ccef75..568ce32fb9 100644 --- a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java +++ b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java @@ -19,6 +19,9 @@ package com.android.launcher3.allapps; import static com.android.launcher3.allapps.PrivateProfileManager.STATE_DISABLED; import static com.android.launcher3.allapps.PrivateProfileManager.STATE_ENABLED; import static com.android.launcher3.allapps.PrivateProfileManager.STATE_TRANSITION; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_TAP; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP; import android.view.View; import android.widget.ImageButton; @@ -63,13 +66,19 @@ public class PrivateSpaceHeaderViewController { quietModeButton.setVisibility(View.VISIBLE); quietModeButton.setImageResource(R.drawable.bg_ps_lock_button); quietModeButton.setOnClickListener( - view -> mPrivateProfileManager.lockPrivateProfile()); + view -> { + mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_LOCK_TAP); + mPrivateProfileManager.lockPrivateProfile(); + }); } case STATE_DISABLED -> { quietModeButton.setVisibility(View.VISIBLE); quietModeButton.setImageResource(R.drawable.bg_ps_unlock_button); quietModeButton.setOnClickListener( - view -> mPrivateProfileManager.unlockPrivateProfile()); + view -> { + mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP); + mPrivateProfileManager.unlockPrivateProfile(); + }); } default -> quietModeButton.setVisibility(View.GONE); } @@ -79,8 +88,11 @@ public class PrivateSpaceHeaderViewController { if (mPrivateProfileManager.getCurrentState() == STATE_ENABLED && mPrivateProfileManager.isPrivateSpaceSettingsAvailable()) { settingsButton.setVisibility(View.VISIBLE); - settingsButton.setOnClickListener(view -> - mPrivateProfileManager.openPrivateSpaceSettings()); + settingsButton.setOnClickListener( + view -> { + mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP); + mPrivateProfileManager.openPrivateSpaceSettings(); + }); } else { settingsButton.setVisibility(View.GONE); } @@ -93,4 +105,8 @@ public class PrivateSpaceHeaderViewController { transitionImage.setVisibility(View.GONE); } } + + PrivateProfileManager getPrivateProfileManager() { + return mPrivateProfileManager; + } } diff --git a/src/com/android/launcher3/allapps/RecyclerViewAnimationController.java b/src/com/android/launcher3/allapps/RecyclerViewAnimationController.java new file mode 100644 index 0000000000..6209393b20 --- /dev/null +++ b/src/com/android/launcher3/allapps/RecyclerViewAnimationController.java @@ -0,0 +1,309 @@ +/* + * 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; + +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 android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.graphics.drawable.Drawable; +import android.util.FloatProperty; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Interpolator; + +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.Utilities; +import com.android.launcher3.model.data.ItemInfo; + +import java.util.List; + +public class RecyclerViewAnimationController { + + private static final String LOG_TAG = "AnimationCtrl"; + + /** + * These values represent points on the [0, 1] animation progress spectrum. They are used to + * animate items in the {@link SearchRecyclerView} and private space container in + * {@link AllAppsRecyclerView}. + */ + protected static final float TOP_CONTENT_FADE_PROGRESS_START = 0.133f; + protected static final float CONTENT_FADE_PROGRESS_DURATION = 0.083f; + protected static final float TOP_BACKGROUND_FADE_PROGRESS_START = 0.633f; + protected static final float BACKGROUND_FADE_PROGRESS_DURATION = 0.15f; + // Progress before next item starts fading. + protected static final float CONTENT_STAGGER = 0.01f; + + protected static final FloatProperty<RecyclerViewAnimationController> PROGRESS = + new FloatProperty<RecyclerViewAnimationController>("expansionProgress") { + @Override + public Float get(RecyclerViewAnimationController controller) { + return controller.getAnimationProgress(); + } + + @Override + public void setValue(RecyclerViewAnimationController controller, float progress) { + controller.setAnimationProgress(progress); + } + }; + + protected final ActivityAllAppsContainerView<?> mAllAppsContainerView; + protected ObjectAnimator mAnimator = null; + private float mAnimatorProgress = 1f; + + public RecyclerViewAnimationController(ActivityAllAppsContainerView<?> allAppsContainerView) { + mAllAppsContainerView = allAppsContainerView; + } + + /** + * Updates the children views of the current recyclerView based on the current animation + * progress. + * + * @return the total height of animating views (may exclude at most one row of app icons + * depending on which recyclerView is being acted upon). + */ + protected int onProgressUpdated(float expansionProgress) { + int numItemsAnimated = 0; + int totalHeight = 0; + int appRowHeight = 0; + boolean appRowComplete = false; + Integer top = null; + AllAppsRecyclerView allAppsRecyclerView = getRecyclerView(); + + for (int i = 0; i < allAppsRecyclerView.getChildCount(); i++) { + View currentView = allAppsRecyclerView.getChildAt(i); + if (currentView == null) { + continue; + } + if (top == null) { + top = currentView.getTop(); + } + int adapterPosition = allAppsRecyclerView.getChildAdapterPosition(currentView); + List<BaseAllAppsAdapter.AdapterItem> allAppsAdapters = allAppsRecyclerView.getApps() + .getAdapterItems(); + if (adapterPosition < 0 || adapterPosition >= allAppsAdapters.size()) { + continue; + } + BaseAllAppsAdapter.AdapterItem adapterItemAtPosition = + allAppsAdapters.get(adapterPosition); + int spanIndex = getSpanIndex(allAppsRecyclerView, adapterPosition); + appRowComplete |= appRowHeight > 0 && spanIndex == 0; + + float backgroundAlpha = 1f; + boolean hasDecorationInfo = adapterItemAtPosition.getDecorationInfo() != null; + boolean shouldAnimate = shouldAnimate(currentView, hasDecorationInfo, appRowComplete); + + if (shouldAnimate) { + if (spanIndex > 0) { + // Animate this item with the previous item on the same row. + numItemsAnimated--; + } + // Adjust background (or decorator) alpha based on start progress and stagger. + backgroundAlpha = getAdjustedBackgroundAlpha(numItemsAnimated); + } + + Drawable background = currentView.getBackground(); + if (background != null && currentView instanceof ViewGroup currentViewGroup) { + currentView.setAlpha(1f); + // Apply content alpha to each child, since the view needs to be fully opaque for + // the background to show properly. + for (int j = 0; j < currentViewGroup.getChildCount(); j++) { + setViewAdjustedContentAlpha(currentViewGroup.getChildAt(j), numItemsAnimated, + shouldAnimate); + } + + // Apply background alpha to the background drawable directly. + background.setAlpha((int) (255 * backgroundAlpha)); + } else { + // Adjust content alpha based on start progress and stagger. + setViewAdjustedContentAlpha(currentView, numItemsAnimated, shouldAnimate); + + // Apply background alpha to decorator if possible. + setAdjustedAdapterItemDecorationBackgroundAlpha( + allAppsRecyclerView.getApps().getAdapterItems().get(adapterPosition), + numItemsAnimated); + + // Apply background alpha to view's background (e.g. for Search Edu card). + if (background != null) { + background.setAlpha((int) (255 * backgroundAlpha)); + } + } + + float scaleY = 1; + if (shouldAnimate) { + scaleY = 1 - getAnimationProgress(); + // Update number of search results that has been animated. + numItemsAnimated++; + } + int scaledHeight = (int) (currentView.getHeight() * scaleY); + currentView.setScaleY(scaleY); + + // For rows with multiple elements, only count the height once and translate elements to + // the same y position. + int y = top + totalHeight; + if (spanIndex > 0) { + // Continuation of an existing row; move this item into the row. + y -= scaledHeight; + } else { + // Start of a new row contributes to total height. + totalHeight += scaledHeight; + if (!shouldAnimate) { + appRowHeight = scaledHeight; + } + } + currentView.setY(y); + } + return totalHeight - appRowHeight; + } + + protected void animateToState(boolean expand, long duration, Runnable onEndRunnable) { + float targetProgress = expand ? 0 : 1; + if (mAnimator != null) { + mAnimator.cancel(); + } + mAnimator = ObjectAnimator.ofFloat(this, PROGRESS, targetProgress); + + TimeInterpolator timeInterpolator = getInterpolator(); + if (timeInterpolator == INSTANT) { + duration = 0; + } + + mAnimator.addListener(forEndCallback(() -> mAnimator = null)); + mAnimator.setDuration(duration).setInterpolator(timeInterpolator); + mAnimator.addListener(forSuccessCallback(onEndRunnable)); + mAnimator.start(); + getRecyclerView().setChildAttachedConsumer(this::onChildAttached); + } + + /** Called just before a child is attached to the RecyclerView. */ + private void onChildAttached(View child) { + // Avoid allocating hardware layers for alpha changes. + child.forceHasOverlappingRendering(false); + child.setPivotY(0); + if (getAnimationProgress() > 0 && getAnimationProgress() < 1) { + // Before the child is rendered, apply the animation including it to avoid flicker. + onProgressUpdated(getAnimationProgress()); + } else { + // Apply default states without processing the full layout. + child.setAlpha(1); + child.setScaleY(1); + child.setTranslationY(0); + int adapterPosition = getRecyclerView().getChildAdapterPosition(child); + List<BaseAllAppsAdapter.AdapterItem> allAppsAdapters = + getRecyclerView().getApps().getAdapterItems(); + if (adapterPosition >= 0 && adapterPosition < allAppsAdapters.size()) { + allAppsAdapters.get(adapterPosition).setDecorationFillAlpha(255); + } + if (child instanceof ViewGroup childGroup) { + for (int i = 0; i < childGroup.getChildCount(); i++) { + childGroup.getChildAt(i).setAlpha(1f); + } + } + if (child.getBackground() != null) { + child.getBackground().setAlpha(255); + } + } + } + + /** @return the column that the view at this position is found (0 assumed if indeterminate). */ + protected int getSpanIndex(AllAppsRecyclerView appsRecyclerView, int adapterPosition) { + if (adapterPosition == NO_POSITION) { + Log.w(LOG_TAG, "Can't determine span index - child not found in adapter"); + return 0; + } + if (!(appsRecyclerView.getAdapter() instanceof AllAppsGridAdapter<?>)) { + Log.e(LOG_TAG, "Search RV doesn't have an AllAppsGridAdapter?"); + // This case shouldn't happen, but for debug devices we will continue to create a more + // visible crash. + if (!Utilities.IS_DEBUG_DEVICE) { + return 0; + } + } + AllAppsGridAdapter<?> adapter = (AllAppsGridAdapter<?>) appsRecyclerView.getAdapter(); + return adapter.getSpanIndex(adapterPosition); + } + + protected TimeInterpolator getInterpolator() { + return DECELERATE_1_7; + } + + protected AllAppsRecyclerView getRecyclerView() { + return mAllAppsContainerView.mAH.get(ActivityAllAppsContainerView.AdapterHolder.MAIN) + .mRecyclerView; + } + + /** Returns true if a transition animation is currently in progress. */ + protected boolean isRunning() { + return mAnimator != null; + } + + /** Should only animate if the view is an app icon and if it has a decoration info. */ + protected boolean shouldAnimate(View view, boolean hasDecorationInfo, + boolean firstAppRowComplete) { + return isAppIcon(view) && hasDecorationInfo; + } + + private float getAdjustedContentAlpha(int itemsAnimated) { + float startContentFadeProgress = Math.max(0, + TOP_CONTENT_FADE_PROGRESS_START - CONTENT_STAGGER * itemsAnimated); + float endContentFadeProgress = Math.min(1, + startContentFadeProgress + CONTENT_FADE_PROGRESS_DURATION); + return 1 - clampToProgress(mAnimatorProgress, + startContentFadeProgress, endContentFadeProgress); + } + + private float getAdjustedBackgroundAlpha(int itemsAnimated) { + float startBackgroundFadeProgress = Math.max(0, + TOP_BACKGROUND_FADE_PROGRESS_START - CONTENT_STAGGER * itemsAnimated); + float endBackgroundFadeProgress = Math.min(1, + startBackgroundFadeProgress + BACKGROUND_FADE_PROGRESS_DURATION); + return 1 - clampToProgress(mAnimatorProgress, + startBackgroundFadeProgress, endBackgroundFadeProgress); + } + + private void setViewAdjustedContentAlpha(View view, int numberOfItemsAnimated, + boolean shouldAnimate) { + view.setAlpha(shouldAnimate ? getAdjustedContentAlpha(numberOfItemsAnimated) : 1f); + } + + private void setAdjustedAdapterItemDecorationBackgroundAlpha( + BaseAllAppsAdapter.AdapterItem adapterItem, int numberOfItemsAnimated) { + adapterItem.setDecorationFillAlpha((int) + (255 * getAdjustedBackgroundAlpha(numberOfItemsAnimated))); + } + + private float getAnimationProgress() { + return mAnimatorProgress; + } + + private void setAnimationProgress(float expansionProgress) { + mAnimatorProgress = expansionProgress; + onProgressUpdated(expansionProgress); + } + + protected boolean isAppIcon(View item) { + return item instanceof BubbleTextView && item.getTag() instanceof ItemInfo + && ((ItemInfo) item.getTag()).itemType == ITEM_TYPE_APPLICATION; + } +} diff --git a/src/com/android/launcher3/allapps/SearchRecyclerView.java b/src/com/android/launcher3/allapps/SearchRecyclerView.java index 9d1dfc0de3..68f9f11d9f 100644 --- a/src/com/android/launcher3/allapps/SearchRecyclerView.java +++ b/src/com/android/launcher3/allapps/SearchRecyclerView.java @@ -27,8 +27,6 @@ import com.android.launcher3.views.RecyclerViewFastScroller; /** A RecyclerView for AllApps Search results. */ public class SearchRecyclerView extends AllAppsRecyclerView { - private Consumer<View> mChildAttachedConsumer; - public SearchRecyclerView(Context context) { this(context, null); } @@ -46,11 +44,6 @@ public class SearchRecyclerView extends AllAppsRecyclerView { super(context, attrs, defStyleAttr, defStyleRes); } - /** This will be called just before a new child is attached to the window. */ - public void setChildAttachedConsumer(Consumer<View> childAttachedConsumer) { - mChildAttachedConsumer = childAttachedConsumer; - } - @Override protected void updatePoolSize() { RecycledViewPool pool = getRecycledViewPool(); @@ -67,12 +60,4 @@ public class SearchRecyclerView extends AllAppsRecyclerView { public RecyclerViewFastScroller getScrollbar() { return null; } - - @Override - public void onChildAttachedToWindow(@NonNull View child) { - if (mChildAttachedConsumer != null) { - mChildAttachedConsumer.accept(child); - } - super.onChildAttachedToWindow(child); - } } diff --git a/src/com/android/launcher3/allapps/SearchTransitionController.java b/src/com/android/launcher3/allapps/SearchTransitionController.java index eb1bc0a4be..d5c3b57788 100644 --- a/src/com/android/launcher3/allapps/SearchTransitionController.java +++ b/src/com/android/launcher3/allapps/SearchTransitionController.java @@ -18,34 +18,21 @@ package com.android.launcher3.allapps; 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 android.animation.ObjectAnimator; import android.animation.TimeInterpolator; -import android.graphics.drawable.Drawable; -import android.util.FloatProperty; -import android.util.Log; import android.view.View; -import android.view.ViewGroup; import android.view.animation.Interpolator; -import com.android.launcher3.BubbleTextView; import com.android.launcher3.R; -import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.model.data.ItemInfo; /** Coordinates the transition between Search and A-Z in All Apps. */ -public class SearchTransitionController { - - private static final String LOG_TAG = "SearchTransitionCtrl"; +public class SearchTransitionController extends RecyclerViewAnimationController { // Interpolator when the user taps the QSB while already in All Apps. private static final Interpolator INTERPOLATOR_WITHIN_ALL_APPS = DECELERATE_1_7; @@ -53,42 +40,10 @@ public class SearchTransitionController { // happening simultaneously. private static final Interpolator INTERPOLATOR_TRANSITIONING_TO_ALL_APPS = INSTANT; - /** - * These values represent points on the [0, 1] animation progress spectrum. They are used to - * animate items in the {@link SearchRecyclerView}. - */ - private static final float TOP_CONTENT_FADE_PROGRESS_START = 0.133f; - private static final float CONTENT_FADE_PROGRESS_DURATION = 0.083f; - private static final float TOP_BACKGROUND_FADE_PROGRESS_START = 0.633f; - private static final float BACKGROUND_FADE_PROGRESS_DURATION = 0.15f; - private static final float CONTENT_STAGGER = 0.01f; // Progress before next item starts fading. - - private static final FloatProperty<SearchTransitionController> SEARCH_TO_AZ_PROGRESS = - new FloatProperty<SearchTransitionController>("searchToAzProgress") { - @Override - public Float get(SearchTransitionController controller) { - return controller.getSearchToAzProgress(); - } - - @Override - public void setValue(SearchTransitionController controller, float progress) { - controller.setSearchToAzProgress(progress); - } - }; - - private final ActivityAllAppsContainerView<?> mAllAppsContainerView; - - private ObjectAnimator mSearchToAzAnimator = null; - private float mSearchToAzProgress = 1f; private boolean mSkipNextAnimationWithinAllApps; public SearchTransitionController(ActivityAllAppsContainerView<?> allAppsContainerView) { - mAllAppsContainerView = allAppsContainerView; - } - - /** Returns true if a transition animation is currently in progress. */ - public boolean isRunning() { - return mSearchToAzAnimator != null; + super(allAppsContainerView); } /** @@ -101,51 +56,31 @@ public class SearchTransitionController { * @param onEndRunnable will be called when the animation finishes, unless another animation is * scheduled in the meantime */ - public void animateToSearchState(boolean goingToSearch, long duration, Runnable onEndRunnable) { - float targetProgress = goingToSearch ? 0 : 1; - - if (mSearchToAzAnimator != null) { - mSearchToAzAnimator.cancel(); - } - - mSearchToAzAnimator = ObjectAnimator.ofFloat(this, SEARCH_TO_AZ_PROGRESS, targetProgress); - boolean inAllApps = mAllAppsContainerView.isInAllApps(); - TimeInterpolator timeInterpolator = - inAllApps ? INTERPOLATOR_WITHIN_ALL_APPS : INTERPOLATOR_TRANSITIONING_TO_ALL_APPS; - if (mSkipNextAnimationWithinAllApps) { - timeInterpolator = INSTANT; - mSkipNextAnimationWithinAllApps = false; - } - if (timeInterpolator == INSTANT) { - duration = 0; // Don't want to animate when coming from QSB. - } - mSearchToAzAnimator.setDuration(duration).setInterpolator(timeInterpolator); - mSearchToAzAnimator.addListener(forEndCallback(() -> mSearchToAzAnimator = null)); + @Override + protected void animateToState(boolean goingToSearch, long duration, Runnable onEndRunnable) { + super.animateToState(goingToSearch, duration, onEndRunnable); if (!goingToSearch) { - mSearchToAzAnimator.addListener(forSuccessCallback(() -> { + mAnimator.addListener(forSuccessCallback(() -> { mAllAppsContainerView.getFloatingHeaderView().setFloatingRowsCollapsed(false); mAllAppsContainerView.getFloatingHeaderView().reset(false /* animate */); mAllAppsContainerView.getAppsRecyclerViewContainer().setTranslationY(0); })); } - mSearchToAzAnimator.addListener(forSuccessCallback(onEndRunnable)); - mAllAppsContainerView.getFloatingHeaderView().setFloatingRowsCollapsed(true); mAllAppsContainerView.getFloatingHeaderView().setVisibility(VISIBLE); mAllAppsContainerView.getFloatingHeaderView().maybeSetTabVisibility(VISIBLE); mAllAppsContainerView.getAppsRecyclerViewContainer().setVisibility(VISIBLE); - getSearchRecyclerView().setVisibility(VISIBLE); - getSearchRecyclerView().setChildAttachedConsumer(this::onSearchChildAttached); - mSearchToAzAnimator.start(); + getRecyclerView().setVisibility(VISIBLE); } - private SearchRecyclerView getSearchRecyclerView() { + @Override + protected SearchRecyclerView getRecyclerView() { return mAllAppsContainerView.getSearchRecyclerView(); } - private void setSearchToAzProgress(float searchToAzProgress) { - mSearchToAzProgress = searchToAzProgress; - int searchHeight = updateSearchRecyclerViewProgress(); + @Override + protected int onProgressUpdated(float searchToAzProgress) { + int searchHeight = super.onProgressUpdated(searchToAzProgress); FloatingHeaderView headerView = mAllAppsContainerView.getFloatingHeaderView(); @@ -171,179 +106,27 @@ public class SearchTransitionController { appsContainer.setTranslationY(appsTranslationY); // Fade apps out with tabs (in 20% of the total animation). appsContainer.setAlpha(clampToProgress(searchToAzProgress, 0.8f, 1f)); + return searchHeight; } /** - * Updates the children views of SearchRecyclerView based on the current animation progress. - * - * @return the total height of animating views (excluding at most one row of app icons). + * Should only animate if the view is not an app icon or if the app row is complete. */ - private int updateSearchRecyclerViewProgress() { - int numSearchResultsAnimated = 0; - int totalHeight = 0; - int appRowHeight = 0; - boolean appRowComplete = false; - Integer top = null; - SearchRecyclerView searchRecyclerView = getSearchRecyclerView(); - - for (int i = 0; i < searchRecyclerView.getChildCount(); i++) { - View searchResultView = searchRecyclerView.getChildAt(i); - if (searchResultView == null) { - continue; - } - - if (top == null) { - top = searchResultView.getTop(); - } - - int adapterPosition = searchRecyclerView.getChildAdapterPosition(searchResultView); - int spanIndex = getSpanIndex(searchRecyclerView, adapterPosition); - appRowComplete |= appRowHeight > 0 && spanIndex == 0; - // We don't animate the first (currently only) app row we see, as that is assumed to be - // predicted/prefix-matched apps. - boolean shouldAnimate = !isAppIcon(searchResultView) || appRowComplete; - - float contentAlpha = 1f; - float backgroundAlpha = 1f; - if (shouldAnimate) { - if (spanIndex > 0) { - // Animate this item with the previous item on the same row. - numSearchResultsAnimated--; - } - - // Adjust content alpha based on start progress and stagger. - float startContentFadeProgress = Math.max(0, - TOP_CONTENT_FADE_PROGRESS_START - - CONTENT_STAGGER * numSearchResultsAnimated); - float endContentFadeProgress = Math.min(1, - startContentFadeProgress + CONTENT_FADE_PROGRESS_DURATION); - contentAlpha = 1 - clampToProgress(mSearchToAzProgress, - startContentFadeProgress, endContentFadeProgress); - - // Adjust background (or decorator) alpha based on start progress and stagger. - float startBackgroundFadeProgress = Math.max(0, - TOP_BACKGROUND_FADE_PROGRESS_START - - CONTENT_STAGGER * numSearchResultsAnimated); - float endBackgroundFadeProgress = Math.min(1, - startBackgroundFadeProgress + BACKGROUND_FADE_PROGRESS_DURATION); - backgroundAlpha = 1 - clampToProgress(mSearchToAzProgress, - startBackgroundFadeProgress, endBackgroundFadeProgress); - - numSearchResultsAnimated++; - } - - Drawable background = searchResultView.getBackground(); - if (background != null - && searchResultView instanceof ViewGroup - && FeatureFlags.ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES.get()) { - searchResultView.setAlpha(1f); - - // Apply content alpha to each child, since the view needs to be fully opaque for - // the background to show properly. - ViewGroup searchResultViewGroup = (ViewGroup) searchResultView; - for (int j = 0; j < searchResultViewGroup.getChildCount(); j++) { - searchResultViewGroup.getChildAt(j).setAlpha(contentAlpha); - } - - // Apply background alpha to the background drawable directly. - background.setAlpha((int) (255 * backgroundAlpha)); - } else { - searchResultView.setAlpha(contentAlpha); - - // Apply background alpha to decorator if possible. - if (adapterPosition != NO_POSITION) { - searchRecyclerView.getApps().getAdapterItems().get(adapterPosition) - .setDecorationFillAlpha((int) (255 * backgroundAlpha)); - } - - // Apply background alpha to view's background (e.g. for Search Edu card). - if (background != null) { - background.setAlpha((int) (255 * backgroundAlpha)); - } - } - - float scaleY = 1; - if (shouldAnimate) { - scaleY = 1 - mSearchToAzProgress; - } - int scaledHeight = (int) (searchResultView.getHeight() * scaleY); - searchResultView.setScaleY(scaleY); - - // For rows with multiple elements, only count the height once and translate elements to - // the same y position. - int y = top + totalHeight; - if (spanIndex > 0) { - // Continuation of an existing row; move this item into the row. - y -= scaledHeight; - } else { - // Start of a new row contributes to total height. - totalHeight += scaledHeight; - if (!shouldAnimate) { - appRowHeight = scaledHeight; - } - } - searchResultView.setY(y); - } - - return totalHeight - appRowHeight; + @Override + protected boolean shouldAnimate(View view, boolean hasDecorationInfo, boolean appRowComplete) { + return !isAppIcon(view) || appRowComplete; } - /** @return the column that the view at this position is found (0 assumed if indeterminate). */ - private int getSpanIndex(SearchRecyclerView searchRecyclerView, int adapterPosition) { - if (adapterPosition == NO_POSITION) { - Log.w(LOG_TAG, "Can't determine span index - child not found in adapter"); - return 0; - } - if (!(searchRecyclerView.getAdapter() instanceof AllAppsGridAdapter<?>)) { - Log.e(LOG_TAG, "Search RV doesn't have an AllAppsGridAdapter?"); - // This case shouldn't happen, but for debug devices we will continue to create a more - // visible crash. - if (!Utilities.IS_DEBUG_DEVICE) { - return 0; - } - } - AllAppsGridAdapter<?> adapter = (AllAppsGridAdapter<?>) searchRecyclerView.getAdapter(); - return adapter.getSpanIndex(adapterPosition); - } - - private boolean isAppIcon(View item) { - return item instanceof BubbleTextView && item.getTag() instanceof ItemInfo - && ((ItemInfo) item.getTag()).itemType == ITEM_TYPE_APPLICATION; - } - - /** Called just before a child is attached to the SearchRecyclerView. */ - private void onSearchChildAttached(View child) { - // Avoid allocating hardware layers for alpha changes. - child.forceHasOverlappingRendering(false); - child.setPivotY(0); - if (mSearchToAzProgress > 0) { - // Before the child is rendered, apply the animation including it to avoid flicker. - updateSearchRecyclerViewProgress(); - } else { - // Apply default states without processing the full layout. - child.setAlpha(1); - child.setScaleY(1); - child.setTranslationY(0); - int adapterPosition = getSearchRecyclerView().getChildAdapterPosition(child); - if (adapterPosition != NO_POSITION) { - getSearchRecyclerView().getApps().getAdapterItems().get(adapterPosition) - .setDecorationFillAlpha(255); - } - if (child instanceof ViewGroup - && FeatureFlags.ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES.get()) { - ViewGroup childGroup = (ViewGroup) child; - for (int i = 0; i < childGroup.getChildCount(); i++) { - childGroup.getChildAt(i).setAlpha(1f); - } - } - if (child.getBackground() != null) { - child.getBackground().setAlpha(255); - } + @Override + protected TimeInterpolator getInterpolator() { + TimeInterpolator timeInterpolator = + mAllAppsContainerView.isInAllApps() + ? INTERPOLATOR_WITHIN_ALL_APPS : INTERPOLATOR_TRANSITIONING_TO_ALL_APPS; + if (mSkipNextAnimationWithinAllApps) { + timeInterpolator = INSTANT; + mSkipNextAnimationWithinAllApps = false; } - } - - private float getSearchToAzProgress() { - return mSearchToAzProgress; + return timeInterpolator; } /** diff --git a/src/com/android/launcher3/allapps/SectionDecorationHandler.java b/src/com/android/launcher3/allapps/SectionDecorationHandler.java new file mode 100644 index 0000000000..f79b82cebb --- /dev/null +++ b/src/com/android/launcher3/allapps/SectionDecorationHandler.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.allapps; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.InsetDrawable; +import android.view.View; + +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; + +import com.android.launcher3.R; +import com.android.launcher3.util.Themes; + +public class SectionDecorationHandler { + + protected final Path mTmpPath = new Path(); + protected final RectF mTmpRect = new RectF(); + + protected final int mCornerGroupRadius; + protected final int mCornerResultRadius; + protected final RectF mBounds = new RectF(); + protected final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + protected final int mFocusAlpha = 255; // main focused item alpha + protected int mFillColor; // grouping color + protected int mFocusColor; // main focused item color + protected float mFillSpacing; + protected int mInlineRadius; + protected Context mContext; + protected float[] mCorners; + protected int mFillAlpha; + protected boolean mIsTopLeftRound; + protected boolean mIsTopRightRound; + protected boolean mIsBottomLeftRound; + protected boolean mIsBottomRightRound; + protected boolean mIsBottomRound; + protected boolean mIsTopRound; + + public SectionDecorationHandler(Context context, int fillAlpha, boolean isTopLeftRound, + boolean isTopRightRound, boolean isBottomLeftRound, + boolean isBottomRightRound) { + + mContext = context; + mFillAlpha = fillAlpha; + mFocusColor = ContextCompat.getColor(context, + R.color.material_color_surface_bright); // UX recommended + mFillColor = ContextCompat.getColor(context, + R.color.material_color_surface_container_high); // UX recommended + + mIsTopLeftRound = isTopLeftRound; + mIsTopRightRound = isTopRightRound; + mIsBottomLeftRound = isBottomLeftRound; + mIsBottomRightRound = isBottomRightRound; + mIsBottomRound = mIsBottomLeftRound && mIsBottomRightRound; + mIsTopRound = mIsTopLeftRound && mIsTopRightRound; + + mCornerGroupRadius = context.getResources().getDimensionPixelSize( + R.dimen.all_apps_recycler_view_decorator_group_radius); + mCornerResultRadius = context.getResources().getDimensionPixelSize( + R.dimen.all_apps_recycler_view_decorator_result_radius); + + mInlineRadius = 0; + mFillSpacing = 0; + initCorners(); + } + + protected void initCorners() { + mCorners = new float[]{ + mIsTopLeftRound ? mCornerGroupRadius : 0, + mIsTopLeftRound ? mCornerGroupRadius : 0, // Top left radius in px + mIsTopRightRound ? mCornerGroupRadius : 0, + mIsTopRightRound ? mCornerGroupRadius : 0, // Top right radius in px + mIsBottomRightRound ? mCornerGroupRadius : 0, + mIsBottomRightRound ? mCornerGroupRadius : 0, // Bottom right + mIsBottomLeftRound ? mCornerGroupRadius : 0, + mIsBottomLeftRound ? mCornerGroupRadius : 0 // Bottom left + }; + } + + protected void setFillAlpha(int fillAlpha) { + mFillAlpha = fillAlpha; + mPaint.setAlpha(mFillAlpha); + } + + protected void onFocusDraw(Canvas canvas, @Nullable View view) { + if (view == null) { + return; + } + mPaint.setColor(mFillColor); + mPaint.setAlpha(mFillAlpha); + int scaledHeight = (int) (view.getHeight() * view.getScaleY()); + mBounds.set(view.getLeft(), view.getY(), view.getRight(), view.getY() + scaledHeight); + onDraw(canvas); + } + + protected void onDraw(Canvas canvas) { + mTmpPath.reset(); + mTmpRect.set(mBounds.left + mFillSpacing, + mBounds.top + mFillSpacing, + mBounds.right - mFillSpacing, + mBounds.bottom - mFillSpacing); + mTmpPath.addRoundRect(mTmpRect, mCorners, Path.Direction.CW); + canvas.drawPath(mTmpPath, mPaint); + } + + /** Sets the right background drawable to the view based on the give decoration info. */ + public void applyBackground(View view, Context context, + @Nullable SectionDecorationInfo decorationInfo, boolean isHighlighted) { + int inset = context.getResources().getDimensionPixelSize( + R.dimen.all_apps_recycler_view_decorator_padding); + float radiusBottom = (decorationInfo == null || decorationInfo.isBottomRound()) ? + mCornerGroupRadius : mCornerResultRadius; + float radiusTop = + (decorationInfo == null || decorationInfo.isTopRound()) ? + mCornerGroupRadius : mCornerResultRadius; + int color = isHighlighted ? mFocusColor : mFillColor; + + GradientDrawable shape = new GradientDrawable(); + shape.setShape(GradientDrawable.RECTANGLE); + shape.setCornerRadii(new float[] { + radiusTop, radiusTop, // top-left + radiusTop, radiusTop, // top-right + radiusBottom, radiusBottom, // bottom-right + radiusBottom, radiusBottom // bottom-left + }); + shape.setColor(color); + + // Setting the background resets the padding, so we cache it and reset it afterwards. + int paddingLeft = view.getPaddingLeft(); + int paddingTop = view.getPaddingTop(); + int paddingRight = view.getPaddingRight(); + int paddingBottom = view.getPaddingBottom(); + + view.setBackground(new InsetDrawable(shape, inset)); + + view.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); + } + + /** + * Section decorator that combines views and draws a single block decoration + */ + public static class UnionDecorationHandler extends SectionDecorationHandler { + + private final int mPaddingLeft; + private final int mPaddingRight; + + public UnionDecorationHandler( + SectionDecorationHandler decorationHandler, + int paddingLeft, int paddingRight) { + super(decorationHandler.mContext, decorationHandler.mFillAlpha, + decorationHandler.mIsTopLeftRound, decorationHandler.mIsTopRightRound, + decorationHandler.mIsBottomLeftRound, decorationHandler.mIsBottomRightRound); + mPaddingLeft = paddingLeft; + mPaddingRight = paddingRight; + } + + /** + * Expands decoration bounds to include child {@link PrivateAppsSectionDecorator} + */ + public void addChild(SectionDecorationHandler child, View view, boolean applyBackground) { + int scaledHeight = (int) (view.getHeight() * view.getScaleY()); + mBounds.union(view.getLeft(), view.getY(), + view.getRight(), view.getY() + scaledHeight); + if (applyBackground) { + applyBackground(view, mContext, null, false); + } + mIsBottomRound |= child.mIsBottomRound; + mIsBottomLeftRound |= child.mIsBottomLeftRound; + mIsBottomRightRound |= child.mIsBottomRightRound; + mIsTopRound |= child.mIsTopRound; + mIsTopLeftRound |= child.mIsTopLeftRound; + mIsTopRightRound |= child.mIsTopRightRound; + } + + /** + * Draws group decoration to canvas + */ + public void onGroupDecorate(Canvas canvas) { + initCorners(); + mBounds.left = mPaddingLeft; + mBounds.right = canvas.getWidth() - mPaddingRight; + mPaint.setColor(mFillColor); + mPaint.setAlpha(mFillAlpha); + onDraw(canvas); + } + } +} diff --git a/src/com/android/launcher3/allapps/SectionDecorationInfo.java b/src/com/android/launcher3/allapps/SectionDecorationInfo.java new file mode 100644 index 0000000000..1fed2b654e --- /dev/null +++ b/src/com/android/launcher3/allapps/SectionDecorationInfo.java @@ -0,0 +1,77 @@ +/* + * 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; + +import android.content.Context; +import android.os.Bundle; + +import androidx.annotation.NonNull; + +public class SectionDecorationInfo { + + public static final int ROUND_NOTHING = 1 << 1; + public static final int ROUND_TOP_LEFT = 1 << 2; + public static final int ROUND_TOP_RIGHT = 1 << 3; + public static final int ROUND_BOTTOM_LEFT = 1 << 4; + public static final int ROUND_BOTTOM_RIGHT = 1 << 5; + public static final int DECORATOR_ALPHA = 255; + + protected boolean mShouldDecorateItemsTogether; + private SectionDecorationHandler mDecorationHandler; + protected boolean mIsTopRound; + protected boolean mIsBottomRound; + + public SectionDecorationInfo(Context context, int roundRegions, boolean decorateTogether) { + mDecorationHandler = + new SectionDecorationHandler(context, DECORATOR_ALPHA, + isFlagEnabled(roundRegions, ROUND_TOP_LEFT), + isFlagEnabled(roundRegions, ROUND_TOP_RIGHT), + isFlagEnabled(roundRegions, ROUND_BOTTOM_LEFT), + isFlagEnabled(roundRegions, ROUND_BOTTOM_RIGHT)); + mShouldDecorateItemsTogether = decorateTogether; + mIsTopRound = isFlagEnabled(roundRegions, ROUND_TOP_LEFT) && + isFlagEnabled(roundRegions, ROUND_TOP_RIGHT); + mIsBottomRound = isFlagEnabled(roundRegions, ROUND_BOTTOM_LEFT) && + isFlagEnabled(roundRegions, ROUND_BOTTOM_RIGHT); + } + + public SectionDecorationInfo(Context context, @NonNull Bundle target, + String targetLayoutType, @NonNull Bundle prevTarget, @NonNull Bundle nextTarget) {} + + public SectionDecorationHandler getDecorationHandler() { + return mDecorationHandler; + } + + private boolean isFlagEnabled(int canonicalFlag, int comparison) { + return (canonicalFlag & comparison) != 0; + } + + /** + * Returns whether multiple {@link SectionDecorationInfo}s with the same sectionId should + * be grouped together. + */ + public boolean shouldDecorateItemsTogether() { + return mShouldDecorateItemsTogether; + } + + public boolean isTopRound() { + return mIsTopRound; + } + + public boolean isBottomRound() { + return mIsBottomRound; + } +} diff --git a/src/com/android/launcher3/apppairs/AppPairIcon.java b/src/com/android/launcher3/apppairs/AppPairIcon.java index 4cf64715f2..1d73441b71 100644 --- a/src/com/android/launcher3/apppairs/AppPairIcon.java +++ b/src/com/android/launcher3/apppairs/AppPairIcon.java @@ -17,11 +17,10 @@ package com.android.launcher3.apppairs; import android.content.Context; -import android.graphics.Canvas; import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.LayoutInflater; +import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -30,9 +29,10 @@ import androidx.annotation.Nullable; import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; +import com.android.launcher3.Reorderable; import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.model.data.FolderInfo; -import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.util.MultiTranslateDelegate; import com.android.launcher3.views.ActivityContext; import java.util.Collections; @@ -44,39 +44,18 @@ import java.util.Comparator; * The app pair icon is two parallel background rectangles with rounded corners. Icons of the two * member apps are set into these rectangles. */ -public class AppPairIcon extends FrameLayout implements DraggableView { - /** - * Design specs -- the below ratios are in relation to the size of a standard app icon. - */ - private static final float OUTER_PADDING_SCALE = 1 / 30f; - private static final float INNER_PADDING_SCALE = 1 / 24f; - private static final float MEMBER_ICON_SCALE = 11 / 30f; - private static final float CENTER_CHANNEL_SCALE = 1 / 30f; - private static final float BIG_RADIUS_SCALE = 1 / 5f; - private static final float SMALL_RADIUS_SCALE = 1 / 15f; - - // App pair icons are slightly smaller than regular icons, so we pad the icon by this much on - // each side. - float mOuterPadding; - // Inside of the icon, the two member apps are padded by this much. - float mInnerPadding; - // The two member apps have icons that are this big (in diameter). - float mMemberIconSize; - // The size of the center channel. - float mCenterChannelSize; - // The large outer radius of the background rectangles. - float mBigRadius; - // The small inner radius of the background rectangles. - float mSmallRadius; - // The app pairs icon appears differently in portrait and landscape. - boolean mIsLandscape; - - private ActivityContext mActivity; +public class AppPairIcon extends FrameLayout implements DraggableView, Reorderable { + // A view that holds the app pair icon graphic. + private AppPairIconGraphic mIconGraphic; // A view that holds the app pair's title. private BubbleTextView mAppPairName; // The underlying ItemInfo that stores info about the app pair members, etc. private FolderInfo mInfo; + // Required for Reorderable -- handles translation and bouncing movements + private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this); + private float mScaleForReorderBounce = 1f; + public AppPairIcon(Context context, AttributeSet attrs) { super(context, attrs); } @@ -103,7 +82,10 @@ public class AppPairIcon extends FrameLayout implements DraggableView { icon.setTag(appPairInfo); icon.setOnClickListener(activity.getItemOnClickListener()); icon.mInfo = appPairInfo; - icon.mActivity = activity; + + // Set up icon drawable area + icon.mIconGraphic = icon.findViewById(R.id.app_pair_icon_graphic); + icon.mIconGraphic.init(activity.getDeviceProfile(), icon); // Set up app pair title icon.mAppPairName = icon.findViewById(R.id.app_pair_icon_name); @@ -121,85 +103,6 @@ public class AppPairIcon extends FrameLayout implements DraggableView { return icon; } - @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); - - // Calculate device-specific measurements - DeviceProfile grid = mActivity.getDeviceProfile(); - int defaultIconSize = grid.iconSizePx; - mOuterPadding = OUTER_PADDING_SCALE * defaultIconSize; - mInnerPadding = INNER_PADDING_SCALE * defaultIconSize; - mMemberIconSize = MEMBER_ICON_SCALE * defaultIconSize; - mCenterChannelSize = CENTER_CHANNEL_SCALE * defaultIconSize; - mBigRadius = BIG_RADIUS_SCALE * defaultIconSize; - mSmallRadius = SMALL_RADIUS_SCALE * defaultIconSize; - mIsLandscape = grid.isLeftRightSplit; - - // Calculate drawable area position - float leftBound = (canvas.getWidth() / 2f) - (defaultIconSize / 2f); - float topBound = getPaddingTop(); - - // Prepare to draw app pair icon background - Drawable background = new AppPairIconBackground(getContext(), this); - background.setBounds(0, 0, defaultIconSize, defaultIconSize); - - // Draw background - canvas.save(); - canvas.translate(leftBound, topBound); - background.draw(canvas); - canvas.restore(); - - // Prepare to draw icons - WorkspaceItemInfo app1 = mInfo.contents.get(0); - WorkspaceItemInfo app2 = mInfo.contents.get(1); - Drawable app1Icon = app1.newIcon(getContext()); - Drawable app2Icon = app2.newIcon(getContext()); - app1Icon.setBounds(0, 0, defaultIconSize, defaultIconSize); - app2Icon.setBounds(0, 0, defaultIconSize, defaultIconSize); - - // Draw first icon - canvas.save(); - canvas.translate(leftBound, topBound); - // The app icons are placed differently depending on device orientation. - if (mIsLandscape) { - canvas.translate( - (defaultIconSize / 2f) - (mCenterChannelSize / 2f) - mInnerPadding - - mMemberIconSize, - (defaultIconSize / 2f) - (mMemberIconSize / 2f) - ); - } else { - canvas.translate( - (defaultIconSize / 2f) - (mMemberIconSize / 2f), - (defaultIconSize / 2f) - (mCenterChannelSize / 2f) - mInnerPadding - - mMemberIconSize - ); - - } - canvas.scale(MEMBER_ICON_SCALE, MEMBER_ICON_SCALE); - app1Icon.draw(canvas); - canvas.restore(); - - // Draw second icon - canvas.save(); - canvas.translate(leftBound, topBound); - // The app icons are placed differently depending on device orientation. - if (mIsLandscape) { - canvas.translate( - (defaultIconSize / 2f) + (mCenterChannelSize / 2f) + mInnerPadding, - (defaultIconSize / 2f) - (mMemberIconSize / 2f) - ); - } else { - canvas.translate( - (defaultIconSize / 2f) - (mMemberIconSize / 2f), - (defaultIconSize / 2f) + (mCenterChannelSize / 2f) + mInnerPadding - ); - } - canvas.scale(MEMBER_ICON_SCALE, MEMBER_ICON_SCALE); - app2Icon.draw(canvas); - canvas.restore(); - } - /** * Returns a formatted accessibility title for app pairs. */ @@ -207,17 +110,52 @@ public class AppPairIcon extends FrameLayout implements DraggableView { return getContext().getString(R.string.app_pair_name_format, app1, app2); } + // Required for DraggableView @Override public int getViewType() { return DRAGGABLE_ICON; } + // Required for DraggableView @Override - public void getWorkspaceVisualDragBounds(Rect bounds) { - mAppPairName.getIconBounds(bounds); + public void getWorkspaceVisualDragBounds(Rect outBounds) { + mIconGraphic.getIconBounds(outBounds); + } + + /** Sets the visibility of the icon's title text */ + public void setTextVisible(boolean visible) { + if (visible) { + mAppPairName.setVisibility(VISIBLE); + } else { + mAppPairName.setVisibility(INVISIBLE); + } + } + + // Required for Reorderable + @Override + public MultiTranslateDelegate getTranslateDelegate() { + return mTranslateDelegate; + } + + // Required for Reorderable + @Override + public void setReorderBounceScale(float scale) { + mScaleForReorderBounce = scale; + super.setScaleX(scale); + super.setScaleY(scale); + } + + // Required for Reorderable + @Override + public float getReorderBounceScale() { + return mScaleForReorderBounce; } public FolderInfo getInfo() { return mInfo; } + + public View getIconDrawableArea() { + return mIconGraphic; + } } diff --git a/src/com/android/launcher3/apppairs/AppPairIconBackground.java b/src/com/android/launcher3/apppairs/AppPairIconBackground.java index 735c82f9d1..4e60ece170 100644 --- a/src/com/android/launcher3/apppairs/AppPairIconBackground.java +++ b/src/com/android/launcher3/apppairs/AppPairIconBackground.java @@ -32,8 +32,8 @@ import com.android.launcher3.R; * A Drawable for the background behind the twin app icons (looks like two rectangles). */ class AppPairIconBackground extends Drawable { - // The icon that we will draw this background on. - private final AppPairIcon icon; + // The underlying view that we are drawing this background on. + private final AppPairIconGraphic icon; private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); /** @@ -44,8 +44,8 @@ class AppPairIconBackground extends Drawable { private static final RectF EMPTY_RECT = new RectF(); private static final float[] ARRAY_OF_ZEROES = new float[8]; - AppPairIconBackground(Context context, AppPairIcon appPairIcon) { - icon = appPairIcon; + AppPairIconBackground(Context context, AppPairIconGraphic iconGraphic) { + icon = iconGraphic; // Set up background paint color TypedArray ta = context.getTheme().obtainStyledAttributes(R.styleable.FolderIconPreview); mBackgroundPaint.setStyle(Paint.Style.FILL); @@ -56,7 +56,7 @@ class AppPairIconBackground extends Drawable { @Override public void draw(Canvas canvas) { - if (icon.mIsLandscape) { + if (icon.isLeftRightSplit()) { drawLeftRightSplit(canvas); } else { drawTopBottomSplit(canvas); @@ -73,29 +73,29 @@ class AppPairIconBackground extends Drawable { // The left half of the background image, excluding center channel RectF leftSide = new RectF( - icon.mOuterPadding, - icon.mOuterPadding, - (width / 2f) - (icon.mCenterChannelSize / 2f), - height - icon.mOuterPadding + 0, + 0, + (width / 2f) - (icon.getCenterChannelSize() / 2f), + height ); // The right half of the background image, excluding center channel RectF rightSide = new RectF( - (width / 2f) + (icon.mCenterChannelSize / 2f), - icon.mOuterPadding, - width - icon.mOuterPadding, - height - icon.mOuterPadding + (width / 2f) + (icon.getCenterChannelSize() / 2f), + 0, + width, + height ); drawCustomRoundedRect(canvas, leftSide, new float[]{ - icon.mBigRadius, icon.mBigRadius, - icon.mSmallRadius, icon.mSmallRadius, - icon.mSmallRadius, icon.mSmallRadius, - icon.mBigRadius, icon.mBigRadius}); + icon.getBigRadius(), icon.getBigRadius(), + icon.getSmallRadius(), icon.getSmallRadius(), + icon.getSmallRadius(), icon.getSmallRadius(), + icon.getBigRadius(), icon.getBigRadius()}); drawCustomRoundedRect(canvas, rightSide, new float[]{ - icon.mSmallRadius, icon.mSmallRadius, - icon.mBigRadius, icon.mBigRadius, - icon.mBigRadius, icon.mBigRadius, - icon.mSmallRadius, icon.mSmallRadius}); + icon.getSmallRadius(), icon.getSmallRadius(), + icon.getBigRadius(), icon.getBigRadius(), + icon.getBigRadius(), icon.getBigRadius(), + icon.getSmallRadius(), icon.getSmallRadius()}); } /** @@ -108,29 +108,29 @@ class AppPairIconBackground extends Drawable { // The top half of the background image, excluding center channel RectF topSide = new RectF( - icon.mOuterPadding, - icon.mOuterPadding, - width - icon.mOuterPadding, - (height / 2f) - (icon.mCenterChannelSize / 2f) + 0, + 0, + width, + (height / 2f) - (icon.getCenterChannelSize() / 2f) ); // The bottom half of the background image, excluding center channel RectF bottomSide = new RectF( - icon.mOuterPadding, - (height / 2f) + (icon.mCenterChannelSize / 2f), - width - icon.mOuterPadding, - height - icon.mOuterPadding + 0, + (height / 2f) + (icon.getCenterChannelSize() / 2f), + width, + height ); drawCustomRoundedRect(canvas, topSide, new float[]{ - icon.mBigRadius, icon.mBigRadius, - icon.mBigRadius, icon.mBigRadius, - icon.mSmallRadius, icon.mSmallRadius, - icon.mSmallRadius, icon.mSmallRadius}); + icon.getBigRadius(), icon.getBigRadius(), + icon.getBigRadius(), icon.getBigRadius(), + icon.getSmallRadius(), icon.getSmallRadius(), + icon.getSmallRadius(), icon.getSmallRadius()}); drawCustomRoundedRect(canvas, bottomSide, new float[]{ - icon.mSmallRadius, icon.mSmallRadius, - icon.mSmallRadius, icon.mSmallRadius, - icon.mBigRadius, icon.mBigRadius, - icon.mBigRadius, icon.mBigRadius}); + icon.getSmallRadius(), icon.getSmallRadius(), + icon.getSmallRadius(), icon.getSmallRadius(), + icon.getBigRadius(), icon.getBigRadius(), + icon.getBigRadius(), icon.getBigRadius()}); } /** @@ -146,7 +146,7 @@ class AppPairIconBackground extends Drawable { c.drawDoubleRoundRect(rect, radii, EMPTY_RECT, ARRAY_OF_ZEROES, mBackgroundPaint); } else { // Fallback rectangle with uniform rounded corners - c.drawRoundRect(rect, icon.mBigRadius, icon.mBigRadius, mBackgroundPaint); + c.drawRoundRect(rect, icon.getBigRadius(), icon.getBigRadius(), mBackgroundPaint); } } diff --git a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt new file mode 100644 index 0000000000..29459796bc --- /dev/null +++ b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.apppairs + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Rect +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.view.Gravity +import android.widget.FrameLayout +import com.android.launcher3.DeviceProfile + +/** + * A FrameLayout marking the area on an [AppPairIcon] where the visual icon will be drawn. One of + * two child UI elements on an [AppPairIcon], along with a BubbleTextView holding the text title. + */ +class AppPairIconGraphic @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : + FrameLayout(context, attrs) { + companion object { + // Design specs -- the below ratios are in relation to the size of a standard app icon. + private const val OUTER_PADDING_SCALE = 1 / 30f + private const val INNER_PADDING_SCALE = 1 / 24f + private const val MEMBER_ICON_SCALE = 11 / 30f + private const val CENTER_CHANNEL_SCALE = 1 / 30f + private const val BIG_RADIUS_SCALE = 1 / 5f + private const val SMALL_RADIUS_SCALE = 1 / 15f + } + + // App pair icons are slightly smaller than regular icons, so we pad the icon by this much on + // each side. + private var outerPadding = 0f + // Inside of the icon, the two member apps are padded by this much. + private var innerPadding = 0f + // The colored background (two rectangles in a square area) is this big. + private var backgroundSize = 0f + // The two member apps have icons that are this big (in diameter). + private var memberIconSize = 0f + // The size of the center channel. + var centerChannelSize = 0f + // The large outer radius of the background rectangles. + var bigRadius = 0f + // The small inner radius of the background rectangles. + var smallRadius = 0f + // The app pairs icon appears differently in portrait and landscape. + var isLeftRightSplit = false + + private lateinit var parentIcon: AppPairIcon + private lateinit var appPairBackground: Drawable + private lateinit var appIcon1: Drawable + private lateinit var appIcon2: Drawable + + fun init(grid: DeviceProfile, icon: AppPairIcon) { + // Calculate device-specific measurements + val defaultIconSize = grid.iconSizePx + outerPadding = OUTER_PADDING_SCALE * defaultIconSize + innerPadding = INNER_PADDING_SCALE * defaultIconSize + backgroundSize = defaultIconSize - outerPadding * 2 + memberIconSize = MEMBER_ICON_SCALE * defaultIconSize + centerChannelSize = CENTER_CHANNEL_SCALE * defaultIconSize + bigRadius = BIG_RADIUS_SCALE * defaultIconSize + smallRadius = SMALL_RADIUS_SCALE * defaultIconSize + isLeftRightSplit = grid.isLeftRightSplit + parentIcon = icon + + appPairBackground = AppPairIconBackground(context, this) + appPairBackground.setBounds(0, 0, backgroundSize.toInt(), backgroundSize.toInt()) + appIcon1 = parentIcon.info.contents[0].newIcon(context) + appIcon2 = parentIcon.info.contents[1].newIcon(context) + appIcon1.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt()) + appIcon2.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt()) + } + + /** Gets this icon graphic's bounds, with respect to the parent icon's coordinate system. */ + fun getIconBounds(outBounds: Rect) { + outBounds.set(0, 0, backgroundSize.toInt(), backgroundSize.toInt()) + outBounds.offset( + // x-coordinate in parent's coordinate system + ((parentIcon.width - backgroundSize) / 2).toInt(), + // y-coordinate in parent's coordinate system + parentIcon.paddingTop + outerPadding.toInt() + ) + } + + override fun dispatchDraw(canvas: Canvas) { + super.dispatchDraw(canvas) + + // Center the drawable area in the larger icon canvas + val lp: LayoutParams = layoutParams as LayoutParams + lp.gravity = Gravity.CENTER_HORIZONTAL + lp.topMargin = outerPadding.toInt() + lp.height = backgroundSize.toInt() + lp.width = backgroundSize.toInt() + layoutParams = lp + + // Draw background + appPairBackground.draw(canvas) + + // Draw first icon + canvas.save() + // The app icons are placed differently depending on device orientation. + if (isLeftRightSplit) { + canvas.translate(innerPadding, height / 2f - memberIconSize / 2f) + } else { + canvas.translate(width / 2f - memberIconSize / 2f, innerPadding) + } + appIcon1.draw(canvas) + canvas.restore() + + // Draw second icon + canvas.save() + // The app icons are placed differently depending on device orientation. + if (isLeftRightSplit) { + canvas.translate( + width - (innerPadding + memberIconSize), + height / 2f - memberIconSize / 2f + ) + } else { + canvas.translate( + width / 2f - memberIconSize / 2f, + height - (innerPadding + memberIconSize) + ) + } + appIcon2.draw(canvas) + canvas.restore() + } +} diff --git a/src/com/android/launcher3/backuprestore/LauncherRestoreEventLogger.kt b/src/com/android/launcher3/backuprestore/LauncherRestoreEventLogger.kt new file mode 100644 index 0000000000..16b185495a --- /dev/null +++ b/src/com/android/launcher3/backuprestore/LauncherRestoreEventLogger.kt @@ -0,0 +1,84 @@ +package com.android.launcher3.backuprestore + +import android.content.Context +import com.android.launcher3.LauncherSettings.Favorites +import com.android.launcher3.R +import com.android.launcher3.util.ResourceBasedOverride + +/** + * Wrapper for logging Restore event metrics for both success and failure to restore the Launcher + * workspace from a backup. + */ +open class LauncherRestoreEventLogger : ResourceBasedOverride { + + companion object { + const val TAG = "LauncherRestoreEventLogger" + + fun newInstance(context: Context?): LauncherRestoreEventLogger { + return ResourceBasedOverride.Overrides.getObject( + LauncherRestoreEventLogger::class.java, + context, + R.string.launcher_restore_event_logger_class + ) + } + } + + /** + * For logging when multiple items of a given data type failed to restore. + * + * @param dataType The data type that was not restored. + * @param count the number of data items that were not restored. + * @param error error type for why the data was not restored. + */ + open fun logLauncherItemsRestoreFailed(dataType: String, count: Int, error: String?) { + // no-op + } + + /** + * For logging when multiple items of a given data type were successfully restored. + * + * @param dataType The data type that was restored. + * @param count the number of data items restored. + */ + open fun logLauncherItemsRestored(dataType: String, count: Int) { + // no-op + } + + /** + * Helper to log successfully restoring a single item from the Favorites table. + * + * @param favoritesId The id of the item type from [Favorites] that was restored. + */ + open fun logSingleFavoritesItemRestored(favoritesId: Int) { + // no-op + } + + /** + * Helper to log a failure to restore a single item from the Favorites table. + * + * @param favoritesId The id of the item type from [Favorites] that was not restored. + * @param error error type for why the data was not restored. + */ + open fun logSingleFavoritesItemRestoreFailed(favoritesId: Int, error: String?) { + // no-op + } + + /** + * Helper to log a failure to restore items from the Favorites table. + * + * @param favoritesId The id of the item type from [Favorites] that was not restored. + * @param count number of items that failed to restore. + * @param error error type for why the data was not restored. + */ + open fun logFavoritesItemsRestoreFailed(favoritesId: Int, count: Int, error: String?) { + // no-op + } + + /** + * Uses the current [restoreEventLogger] to report its results to the [backupManager]. Use when + * done restoring items for Launcher. + */ + open fun reportLauncherRestoreResults() { + // no-op + } +} diff --git a/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java b/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java index 7deb65315a..8d0cf138ca 100644 --- a/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java +++ b/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java @@ -48,28 +48,24 @@ public class MulticellReorderAlgorithm extends ReorderAlgorithm { } @Override - public ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY, int minSpanX, - int minSpanY, int spanX, int spanY) { + public ItemConfiguration closestEmptySpaceReorder(ReorderParameters reorderParameters) { return removeSeamFromSolution(simulateSeam( - () -> super.closestEmptySpaceReorder(pixelX, pixelY, minSpanX, minSpanY, spanX, - spanY))); + () -> super.closestEmptySpaceReorder(reorderParameters)) + ); } @Override - public ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX, - int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX, - ItemConfiguration solution) { + public ItemConfiguration findReorderSolution(ReorderParameters reorderParameters, + boolean decX) { return removeSeamFromSolution(simulateSeam( - () -> super.findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, - direction, dragView, decX, solution))); + () -> super.findReorderSolution(reorderParameters, decX))); } @Override - public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX, - int spanY, - View dragView) { - return removeSeamFromSolution(simulateSeam( - () -> super.dropInPlaceSolution(pixelX, pixelY, spanX, spanY, dragView))); + public ItemConfiguration dropInPlaceSolution(ReorderParameters reorderParameters) { + return removeSeamFromSolution( + simulateSeam(() -> super.dropInPlaceSolution(reorderParameters)) + ); } void addSeam() { diff --git a/src/com/android/launcher3/celllayout/ReorderAlgorithm.java b/src/com/android/launcher3/celllayout/ReorderAlgorithm.java index 7385c0a3cf..8754b748e0 100644 --- a/src/com/android/launcher3/celllayout/ReorderAlgorithm.java +++ b/src/com/android/launcher3/celllayout/ReorderAlgorithm.java @@ -45,36 +45,28 @@ public class ReorderAlgorithm { * This method differs from closestEmptySpaceReorder and dropInPlaceSolution because this method * will move items around and will change the shape of the item if possible to try to find a * solution. - * + * <p> * When changing the size of the widget this method will try first subtracting -1 in the x * dimension and then subtracting -1 in the y dimension until finding a possible solution or * until it no longer can reduce the span. * - * @param pixelX X coordinate in pixels in the screen - * @param pixelY Y coordinate in pixels in the screen - * @param minSpanX minimum possible horizontal span it will try to find a solution for. - * @param minSpanY minimum possible vertical span it will try to find a solution for. - * @param spanX horizontal cell span - * @param spanY vertical cell span - * @param direction direction in which it will try to push the items intersecting the desired - * view - * @param dragView view being dragged in reorder - * @param decX whether it will decrease the horizontal or vertical span if it can't find a - * solution for the current span. - * @param solution variable to store the solution + * @param decX whether it will decrease the horizontal or vertical span if it can't find a + * solution for the current span. * @return the same solution variable */ - public ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX, - int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX, - ItemConfiguration solution) { - return findReorderSolutionRecursive(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, - direction, dragView, decX, solution); + public ItemConfiguration findReorderSolution(ReorderParameters reorderParameters, + boolean decX) { + return findReorderSolutionRecursive(reorderParameters.getPixelX(), + reorderParameters.getPixelY(), reorderParameters.getMinSpanX(), + reorderParameters.getMinSpanY(), reorderParameters.getSpanX(), + reorderParameters.getSpanY(), mCellLayout.mDirectionVector, + reorderParameters.getDragView(), decX, reorderParameters.getSolution()); } - private ItemConfiguration findReorderSolutionRecursive(int pixelX, int pixelY, - int minSpanX, int minSpanY, int spanX, int spanY, int[] direction, View dragView, - boolean decX, ItemConfiguration solution) { + private ItemConfiguration findReorderSolutionRecursive(int pixelX, int pixelY, int minSpanX, + int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX, + ItemConfiguration solution) { // Copy the current state into the solution. This solution will be manipulated as necessary. mCellLayout.copyCurrentStateToSolution(solution); // Copy the current occupied array into the temporary occupied array. This array will be @@ -89,8 +81,8 @@ public class ReorderAlgorithm { boolean success; // First we try the exact nearest position of the item being dragged, // we will then want to try to move this around to other neighbouring positions - success = rearrangementExists(result[0], result[1], spanX, spanY, direction, - dragView, solution); + success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView, + solution); if (!success) { // We try shrinking the widget down to size in an alternating pattern, shrink 1 in @@ -135,10 +127,11 @@ public class ReorderAlgorithm { // and not by the views hash which is "random". // The views are sorted twice, once for the X position and a second time for the Y position // to ensure same order everytime. - Comparator comparator = Comparator.comparing(view -> - ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellX()) - .thenComparing(view -> - ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellY()); + Comparator comparator = Comparator.comparing( + view -> ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellX() + ).thenComparing( + view -> ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellY() + ); List<View> views = solution.map.keySet().stream().sorted(comparator).toList(); for (View child : views) { if (child == ignoreView) continue; @@ -158,15 +151,13 @@ public class ReorderAlgorithm { // First we try to find a solution which respects the push mechanic. That is, // we try to find a solution such that no displaced item travels through another item // without also displacing that item. - if (attemptPushInDirection(intersectingViews, occupiedRect, direction, - ignoreView, + if (attemptPushInDirection(intersectingViews, occupiedRect, direction, ignoreView, solution)) { return true; } // Next we try moving the views as a block, but without requiring the push mechanic. - if (addViewsToTempLocation(intersectingViews, occupiedRect, direction, - ignoreView, + if (addViewsToTempLocation(intersectingViews, occupiedRect, direction, ignoreView, solution)) { return true; } @@ -180,8 +171,8 @@ public class ReorderAlgorithm { return true; } - private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop, - int[] direction, ItemConfiguration currentState) { + private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop, int[] direction, + ItemConfiguration currentState) { CellAndSpan c = currentState.map.get(v); boolean success = false; mCellLayout.mTmpOccupied.markCells(c, false); @@ -294,6 +285,11 @@ public class ReorderAlgorithm { return foundSolution; } + private void revertDir(int[] direction) { + direction[0] *= -1; + direction[1] *= -1; + } + // This method tries to find a reordering solution which satisfies the push mechanic by trying // to push items in each of the cardinal directions, in an order based on the direction vector // passed. @@ -302,91 +298,36 @@ public class ReorderAlgorithm { if ((Math.abs(direction[0]) + Math.abs(direction[1])) > 1) { // If the direction vector has two non-zero components, we try pushing // separately in each of the components. - int temp = direction[1]; - direction[1] = 0; - - if (pushViewsToTempLocation(intersectingViews, occupied, direction, - ignoreView, solution)) { - return true; - } - direction[1] = temp; - temp = direction[0]; - direction[0] = 0; - - if (pushViewsToTempLocation(intersectingViews, occupied, direction, - ignoreView, solution)) { - return true; - } - // Revert the direction - direction[0] = temp; - - // Now we try pushing in each component of the opposite direction - direction[0] *= -1; - direction[1] *= -1; - temp = direction[1]; - direction[1] = 0; - if (pushViewsToTempLocation(intersectingViews, occupied, direction, - ignoreView, solution)) { - return true; - } - - direction[1] = temp; - temp = direction[0]; - direction[0] = 0; - if (pushViewsToTempLocation(intersectingViews, occupied, direction, - ignoreView, solution)) { - return true; + int temp; + for (int j = 0; j < 2; j++) { + for (int i = 1; i >= 0; i--) { + temp = direction[i]; + direction[i] = 0; + if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView, + solution)) { + return true; + } + direction[i] = temp; + } + revertDir(direction); } - // revert the direction - direction[0] = temp; - direction[0] *= -1; - direction[1] *= -1; - } else { // If the direction vector has a single non-zero component, we push first in the // direction of the vector - if (pushViewsToTempLocation(intersectingViews, occupied, direction, - ignoreView, solution)) { - return true; - } - // Then we try the opposite direction - direction[0] *= -1; - direction[1] *= -1; - if (pushViewsToTempLocation(intersectingViews, occupied, direction, - ignoreView, solution)) { - return true; - } - // Switch the direction back - direction[0] *= -1; - direction[1] *= -1; - - // If we have failed to find a push solution with the above, then we try - // to find a solution by pushing along the perpendicular axis. - - // Swap the components - int temp = direction[1]; - direction[1] = direction[0]; - direction[0] = temp; - if (pushViewsToTempLocation(intersectingViews, occupied, direction, - ignoreView, solution)) { - return true; - } - - // Then we try the opposite direction - direction[0] *= -1; - direction[1] *= -1; - if (pushViewsToTempLocation(intersectingViews, occupied, direction, - ignoreView, solution)) { - return true; + int temp; + for (int j = 0; j < 2; j++) { + for (int i = 0; i < 2; i++) { + if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView, + solution)) { + return true; + } + revertDir(direction); + } + // Swap the components + temp = direction[1]; + direction[1] = direction[0]; + direction[0] = temp; } - // Switch the direction back - direction[0] *= -1; - direction[1] *= -1; - - // Swap the components back - temp = direction[1]; - direction[1] = direction[0]; - direction[0] = temp; } return false; } @@ -446,63 +387,59 @@ public class ReorderAlgorithm { /** * Returns a "reorder" if there is empty space without rearranging anything. * - * @param pixelX X coordinate in pixels in the screen - * @param pixelY Y coordinate in pixels in the screen - * @param spanX horizontal cell span - * @param spanY vertical cell span - * @param dragView view being dragged in reorder * @return the configuration that represents the found reorder */ - public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX, - int spanY, View dragView) { - int[] result = mCellLayout.findNearestAreaIgnoreOccupied(pixelX, pixelY, spanX, spanY, - new int[2]); + public ItemConfiguration dropInPlaceSolution(ReorderParameters reorderParameters) { + int[] result = mCellLayout.findNearestAreaIgnoreOccupied(reorderParameters.getPixelX(), + reorderParameters.getPixelY(), reorderParameters.getSpanX(), + reorderParameters.getSpanY(), new int[2]); ItemConfiguration solution = new ItemConfiguration(); mCellLayout.copyCurrentStateToSolution(solution); solution.isSolution = !isConfigurationRegionOccupied( - new Rect(result[0], result[1], result[0] + spanX, result[1] + spanY), - solution, - dragView - ); + new Rect(result[0], result[1], result[0] + reorderParameters.getSpanX(), + result[1] + reorderParameters.getSpanY()), solution, + reorderParameters.getDragView()); if (!solution.isSolution) { return solution; } solution.cellX = result[0]; solution.cellY = result[1]; - solution.spanX = spanX; - solution.spanY = spanY; + solution.spanX = reorderParameters.getSpanX(); + solution.spanY = reorderParameters.getSpanY(); return solution; } - private boolean isConfigurationRegionOccupied(Rect region, - ItemConfiguration configuration, View ignoreView) { - return configuration.map.entrySet() + private boolean isConfigurationRegionOccupied(Rect region, ItemConfiguration configuration, + View ignoreView) { + return configuration.map + .entrySet() .stream() .filter(entry -> entry.getKey() != ignoreView) .map(Entry::getValue) - .anyMatch(cellAndSpan -> region.intersect(cellAndSpan.cellX, cellAndSpan.cellY, + .anyMatch(cellAndSpan -> region.intersect( + cellAndSpan.cellX, + cellAndSpan.cellY, cellAndSpan.cellX + cellAndSpan.spanX, - cellAndSpan.cellY + cellAndSpan.spanY)); + cellAndSpan.cellY + cellAndSpan.spanY + ) + ); } /** * Returns a "reorder" where we simply drop the item in the closest empty space, without moving * any other item in the way. * - * @param pixelX X coordinate in pixels in the screen - * @param pixelY Y coordinate in pixels in the screen - * @param spanX horizontal cell span - * @param spanY vertical cell span * @return the configuration that represents the found reorder */ - public ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY, - int minSpanX, int minSpanY, int spanX, int spanY) { + public ItemConfiguration closestEmptySpaceReorder(ReorderParameters reorderParameters) { ItemConfiguration solution = new ItemConfiguration(); int[] result = new int[2]; int[] resultSpan = new int[2]; - mCellLayout.findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, result, - resultSpan); + mCellLayout.findNearestVacantArea(reorderParameters.getPixelX(), + reorderParameters.getPixelY(), reorderParameters.getMinSpanX(), + reorderParameters.getMinSpanY(), reorderParameters.getSpanX(), + reorderParameters.getSpanY(), result, resultSpan); if (result[0] >= 0 && result[1] >= 0) { mCellLayout.copyCurrentStateToSolution(solution); solution.cellX = result[0]; @@ -521,32 +458,19 @@ public class ReorderAlgorithm { * the workspace to make space for the new item, this function return a solution for that * reorder. * - * @param pixelX X coordinate in the screen of the dragView in pixels - * @param pixelY Y coordinate in the screen of the dragView in pixels - * @param minSpanX minimum horizontal span the item can be shrunk to - * @param minSpanY minimum vertical span the item can be shrunk to - * @param spanX occupied horizontal span - * @param spanY occupied vertical span - * @param dragView the view of the item being draged * @return returns a solution for the given parameters, the solution contains all the icons and * the locations they should be in the given solution. */ - public ItemConfiguration calculateReorder(int pixelX, int pixelY, int minSpanX, - int minSpanY, int spanX, int spanY, View dragView) { - getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView, - mCellLayout.mDirectionVector); + public ItemConfiguration calculateReorder(ReorderParameters reorderParameters) { + getDirectionVectorForDrop(reorderParameters, mCellLayout.mDirectionVector); - ItemConfiguration dropInPlaceSolution = dropInPlaceSolution(pixelX, pixelY, spanX, spanY, - dragView); + ItemConfiguration dropInPlaceSolution = dropInPlaceSolution(reorderParameters); // Find a solution involving pushing / displacing any items in the way - ItemConfiguration swapSolution = findReorderSolution(pixelX, pixelY, minSpanX, - minSpanY, spanX, spanY, mCellLayout.mDirectionVector, dragView, true, - new ItemConfiguration()); + ItemConfiguration swapSolution = findReorderSolution(reorderParameters, true); // We attempt the approach which doesn't shuffle views at all - ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(pixelX, pixelY, minSpanX, - minSpanY, spanX, spanY); + ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(reorderParameters); // If the reorder solution requires resizing (shrinking) the item being dropped, we instead // favor a solution in which the item is not resized, but @@ -586,21 +510,26 @@ public class ReorderAlgorithm { * those cells. Instead we use some heuristics to often lock the vector to up, down, left * or right, which helps make pushing feel right. */ - private void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX, - int spanY, View dragView, int[] resultDirection) { + public void getDirectionVectorForDrop(ReorderParameters reorderParameters, + int[] resultDirection) { //TODO(adamcohen) b/151776141 use the items visual center for the direction vector int[] targetDestination = new int[2]; - mCellLayout.findNearestAreaIgnoreOccupied(dragViewCenterX, dragViewCenterY, spanX, spanY, - targetDestination); + mCellLayout.findNearestAreaIgnoreOccupied(reorderParameters.getPixelX(), + reorderParameters.getPixelY(), reorderParameters.getSpanX(), + reorderParameters.getSpanY(), targetDestination); Rect dragRect = new Rect(); - mCellLayout.cellToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect); - dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY()); + mCellLayout.cellToRect(targetDestination[0], targetDestination[1], + reorderParameters.getSpanX(), reorderParameters.getSpanY(), dragRect); + dragRect.offset(reorderParameters.getPixelX() - dragRect.centerX(), + reorderParameters.getPixelY() - dragRect.centerY()); Rect region = new Rect(targetDestination[0], targetDestination[1], - targetDestination[0] + spanX, targetDestination[1] + spanY); - Rect dropRegionRect = mCellLayout.getIntersectingRectanglesInRegion(region, dragView); + targetDestination[0] + reorderParameters.getSpanX(), + targetDestination[1] + reorderParameters.getSpanY()); + Rect dropRegionRect = mCellLayout.getIntersectingRectanglesInRegion(region, + reorderParameters.getDragView()); if (dropRegionRect == null) dropRegionRect = new Rect(region); int dropRegionSpanX = dropRegionRect.width(); @@ -609,13 +538,17 @@ public class ReorderAlgorithm { mCellLayout.cellToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(), dropRegionRect.height(), dropRegionRect); - int deltaX = (dropRegionRect.centerX() - dragViewCenterX) / spanX; - int deltaY = (dropRegionRect.centerY() - dragViewCenterY) / spanY; + int deltaX = (dropRegionRect.centerX() - reorderParameters.getPixelX()) + / reorderParameters.getSpanX(); + int deltaY = (dropRegionRect.centerY() - reorderParameters.getPixelY()) + / reorderParameters.getSpanY(); - if (dropRegionSpanX == mCellLayout.getCountX() || spanX == mCellLayout.getCountX()) { + if (dropRegionSpanX == mCellLayout.getCountX() + || reorderParameters.getSpanX() == mCellLayout.getCountX()) { deltaX = 0; } - if (dropRegionSpanY == mCellLayout.getCountY() || spanY == mCellLayout.getCountY()) { + if (dropRegionSpanY == mCellLayout.getCountY() + || reorderParameters.getSpanY() == mCellLayout.getCountY()) { deltaY = 0; } diff --git a/src/com/android/launcher3/celllayout/ReorderParameters.kt b/src/com/android/launcher3/celllayout/ReorderParameters.kt new file mode 100644 index 0000000000..3fdf35caad --- /dev/null +++ b/src/com/android/launcher3/celllayout/ReorderParameters.kt @@ -0,0 +1,30 @@ +/* + * 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.celllayout + +import android.view.View + +class ReorderParameters( + val pixelX: Int, + val pixelY: Int, + val spanX: Int, + val spanY: Int, + val minSpanX: Int, + val minSpanY: Int, + val dragView: View?, + val solution: ItemConfiguration +) {} diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index bed6efbecf..e1816cb1e5 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -219,9 +219,6 @@ public final class FeatureFlags { public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag(270393108, "NOTIFY_CRASHES", TEAMFOOD, "Sends a notification whenever launcher encounters an uncaught exception."); - public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(270395798, - "ENABLE_TRANSIENT_TASKBAR", ENABLED, "Enables transient taskbar."); - public static final boolean ENABLE_TASKBAR_NAVBAR_UNIFICATION = enableTaskbarNavbarUnification(); @@ -270,10 +267,6 @@ public final class FeatureFlags { public static final BooleanFlag IME_STICKY_SNACKBAR_EDU = getDebugFlag(270391693, "IME_STICKY_SNACKBAR_EDU", ENABLED, "Show sticky IME edu in AllApps"); - public static final BooleanFlag ENABLE_PEOPLE_TILE_PREVIEW = getDebugFlag(270391653, - "ENABLE_PEOPLE_TILE_PREVIEW", DISABLED, - "Experimental: Shows conversation shortcuts on home screen as search results"); - public static final BooleanFlag FOLDER_NAME_MAJORITY_RANKING = getDebugFlag(270391638, "FOLDER_NAME_MAJORITY_RANKING", ENABLED, "Suggests folder names based on majority based ranking."); @@ -315,7 +308,7 @@ public final class FeatureFlags { "Long press of nav handle is instantly triggered if deep press is detected."); public static final IntFlag LPNH_HAPTIC_HINT_DELAY = - getIntFlag(309972570, "LPNH_HAPTIC_HINT_ITERATIONS", 0, + getIntFlag(309972570, "LPNH_HAPTIC_HINT_DELAY", 0, "Delay before haptic hint starts."); // TODO(Block 17): Clean up flags @@ -491,10 +484,10 @@ public final class FeatureFlags { // TODO(Block 33): Clean up flags public static final BooleanFlag ENABLE_ALL_APPS_RV_PREINFLATION = getDebugFlag(288161355, - "ENABLE_ALL_APPS_RV_PREINFLATION", ENABLED, + "ENABLE_ALL_APPS_RV_PREINFLATION", TEAMFOOD, "Enables preinflating all apps icons to avoid scrolling jank."); public static final BooleanFlag ALL_APPS_GONE_VISIBILITY = getDebugFlag(291651514, - "ALL_APPS_GONE_VISIBILITY", ENABLED, + "ALL_APPS_GONE_VISIBILITY", TEAMFOOD, "Set all apps container view's hidden visibility to GONE instead of INVISIBLE."); // TODO(Block 34): Empty block diff --git a/src/com/android/launcher3/dragndrop/AddItemDragLayer.java b/src/com/android/launcher3/dragndrop/SimpleDragLayer.java index 5b52c3d57c..e42ba7261b 100644 --- a/src/com/android/launcher3/dragndrop/AddItemDragLayer.java +++ b/src/com/android/launcher3/dragndrop/SimpleDragLayer.java @@ -20,18 +20,20 @@ import android.content.Context; import android.util.AttributeSet; import com.android.launcher3.util.TouchController; +import com.android.launcher3.views.ActivityContext; import com.android.launcher3.views.BaseDragLayer; /** - * Drag layer for {@link AddItemActivity}. + * A concrete {@link BaseDragLayer} that creates an empty list of {@link TouchController}s. + * @param <T> The {@link ActivityContext} hosting the drag layer. */ -public class AddItemDragLayer extends BaseDragLayer<AddItemActivity> { +public class SimpleDragLayer<T extends Context & ActivityContext> extends BaseDragLayer<T> { - public AddItemDragLayer(Context context, AttributeSet attrs) { + public SimpleDragLayer(Context context, AttributeSet attrs) { this(context, attrs, /*alphaChannelCount= */ 1); } - public AddItemDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) { + public SimpleDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) { super(context, attrs, alphaChannelCount); } diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 8bf7ec234e..084f829884 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -210,7 +210,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo @ViewDebug.IntToString(from = STATE_OPEN, to = "STATE_OPEN"), }) private int mState = STATE_CLOSED; - private OnFolderStateChangedListener mOnFolderStateChangedListener; + private final List<OnFolderStateChangedListener> mOnFolderStateChangedListeners = + new ArrayList<>(); + private OnFolderStateChangedListener mPriorityOnFolderStateChangedListener; @ViewDebug.ExportedProperty(category = "launcher") private boolean mRearrangeOnClose = false; boolean mItemsInvalidated = false; @@ -1082,7 +1084,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo private void updateItemLocationsInDatabaseBatch(boolean isBind) { FolderGridOrganizer verifier = new FolderGridOrganizer( - mActivityContext.getDeviceProfile().inv).setFolderInfo(mInfo); + mActivityContext.getDeviceProfile()).setFolderInfo(mInfo); ArrayList<ItemInfo> items = new ArrayList<>(); int total = mInfo.contents.size(); @@ -1381,7 +1383,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo @Override public void onAdd(WorkspaceItemInfo item, int rank) { FolderGridOrganizer verifier = new FolderGridOrganizer( - mActivityContext.getDeviceProfile().inv).setFolderInfo(mInfo); + mActivityContext.getDeviceProfile()).setFolderInfo(mInfo); verifier.updateRankAndPos(item, rank); mLauncherDelegate.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX, item.cellY); @@ -1665,18 +1667,43 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return windowBottomPx - folderBottomPx; } + /** + * Save this listener for the special case of when we update the state and concurrently + * add another listener to {@link #mOnFolderStateChangedListeners} to avoid a + * ConcurrentModificationException + */ + public void setPriorityOnFolderStateChangedListener(OnFolderStateChangedListener listener) { + mPriorityOnFolderStateChangedListener = listener; + } + private void setState(@FolderState int newState) { mState = newState; - if (mOnFolderStateChangedListener != null) { - mOnFolderStateChangedListener.onFolderStateChanged(mState); + if (mPriorityOnFolderStateChangedListener != null) { + mPriorityOnFolderStateChangedListener.onFolderStateChanged(mState); + } + for (OnFolderStateChangedListener listener : mOnFolderStateChangedListeners) { + if (listener != null) { + listener.onFolderStateChanged(mState); + } + } + } + + /** + * Adds the provided listener to the running list of Folder listeners + * {@link #mOnFolderStateChangedListeners} + */ + public void addOnFolderStateChangedListener(@Nullable OnFolderStateChangedListener listener) { + if (listener != null) { + mOnFolderStateChangedListeners.add(listener); } } - public void setOnFolderStateChangedListener(@Nullable OnFolderStateChangedListener listener) { - mOnFolderStateChangedListener = listener; + /** Removes the provided listener from the running list of Folder listeners */ + public void removeOnFolderStateChangedListener(OnFolderStateChangedListener listener) { + mOnFolderStateChangedListeners.remove(listener); } - /** Listener that can be registered via {@link Folder#setOnFolderStateChangedListener} */ + /** Listener that can be registered via {@link #addOnFolderStateChangedListener} */ public interface OnFolderStateChangedListener { /** See {@link Folder.FolderState} */ void onFolderStateChanged(@FolderState int newState); diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java index 9e2e2bf5fc..a91373ba49 100644 --- a/src/com/android/launcher3/folder/FolderAnimationManager.java +++ b/src/com/android/launcher3/folder/FolderAnimationManager.java @@ -96,7 +96,7 @@ public class FolderAnimationManager { mContext = folder.getContext(); mDeviceProfile = folder.mActivityContext.getDeviceProfile(); - mPreviewVerifier = new FolderGridOrganizer(mDeviceProfile.inv); + mPreviewVerifier = new FolderGridOrganizer(mDeviceProfile); mIsOpening = isOpening; diff --git a/src/com/android/launcher3/folder/FolderGridOrganizer.java b/src/com/android/launcher3/folder/FolderGridOrganizer.java index 4be82ed8d3..cc247619e4 100644 --- a/src/com/android/launcher3/folder/FolderGridOrganizer.java +++ b/src/com/android/launcher3/folder/FolderGridOrganizer.java @@ -20,7 +20,7 @@ import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_I import android.graphics.Point; -import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; @@ -41,11 +41,13 @@ public class FolderGridOrganizer { private int mCountX; private int mCountY; private boolean mDisplayingUpperLeftQuadrant = false; + private static final int PREVIEW_MAX_ROWS = 2; + private static final int PREVIEW_MAX_COLUMNS = 2; /** * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work. */ - public FolderGridOrganizer(InvariantDeviceProfile profile) { + public FolderGridOrganizer(DeviceProfile profile) { mMaxCountX = profile.numFolderColumns; mMaxCountY = profile.numFolderRows; mMaxItemsPerPage = mMaxCountX * mMaxCountY; @@ -127,6 +129,7 @@ public class FolderGridOrganizer { /** * Updates the item's cellX, cellY and rank corresponding to the provided rank. + * * @return true if there was any change */ public boolean updateRankAndPos(ItemInfo item, int rank) { @@ -189,7 +192,7 @@ public class FolderGridOrganizer { if (page > 0 || mDisplayingUpperLeftQuadrant) { int col = rank % mCountX; int row = rank / mCountX; - return col < 2 && row < 2; + return col < PREVIEW_MAX_COLUMNS && row < PREVIEW_MAX_ROWS; } return rank < MAX_NUM_ITEMS_IN_PREVIEW; } diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index cb1dc4fb2a..f058ae4d02 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -86,7 +86,6 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; - /** * An icon that can appear on in the workspace representing an {@link Folder}. */ @@ -221,7 +220,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel icon.setAccessibilityDelegate(activity.getAccessibilityDelegate()); - icon.mPreviewVerifier = new FolderGridOrganizer(activity.getDeviceProfile().inv); + icon.mPreviewVerifier = new FolderGridOrganizer(activity.getDeviceProfile()); icon.mPreviewVerifier.setFolderInfo(folderInfo); icon.updatePreviewItems(false); @@ -634,6 +633,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } } + /** Sets the visibility of the icon's title text */ public void setTextVisible(boolean visible) { if (visible) { mFolderName.setVisibility(VISIBLE); diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java index 36e5e1b106..f2bed925c1 100644 --- a/src/com/android/launcher3/folder/FolderPagedView.java +++ b/src/com/android/launcher3/folder/FolderPagedView.java @@ -37,8 +37,6 @@ import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; @@ -101,14 +99,15 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> implements Cli public FolderPagedView(Context context, AttributeSet attrs) { super(context, attrs); - InvariantDeviceProfile profile = LauncherAppState.getIDP(context); + ActivityContext activityContext = ActivityContext.lookupContext(context); + DeviceProfile profile = activityContext.getDeviceProfile(); mOrganizer = new FolderGridOrganizer(profile); mIsRtl = Utilities.isRtl(getResources()); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); mFocusIndicatorHelper = new ViewGroupFocusHelper(this); - mViewCache = ActivityContext.lookupContext(context).getViewCache(); + mViewCache = activityContext.getViewCache(); } public void setFolder(Folder folder) { diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java index f90779c725..d8388c2709 100644 --- a/src/com/android/launcher3/logging/StatsLogManager.java +++ b/src/com/android/launcher3/logging/StatsLogManager.java @@ -549,12 +549,6 @@ public class StatsLogManager implements ResourceBasedOverride { @UiEvent(doc = "Launcher item drop failed since there was not enough room on the screen.") LAUNCHER_ITEM_DROP_FAILED_INSUFFICIENT_SPACE(872), - @UiEvent(doc = "User long pressed on the taskbar background to hide the taskbar") - LAUNCHER_TASKBAR_LONGPRESS_HIDE(896), - - @UiEvent(doc = "User long pressed on the taskbar gesture handle to show the taskbar") - LAUNCHER_TASKBAR_LONGPRESS_SHOW(897), - @UiEvent(doc = "User clicks on the search icon on header to launch search in app.") LAUNCHER_ALLAPPS_SEARCHINAPP_LAUNCH(913), @@ -693,7 +687,16 @@ public class StatsLogManager implements ResourceBasedOverride { LAUNCHER_TASKBAR_PINNED(1490), @UiEvent(doc = "User has unpinned taskbar using taskbar divider menu") - LAUNCHER_TASKBAR_UNPINNED(1491) + LAUNCHER_TASKBAR_UNPINNED(1491), + + @UiEvent(doc = "User tapped private space lock button") + LAUNCHER_PRIVATE_SPACE_LOCK_TAP(1548), + + @UiEvent(doc = "User tapped private space unlock button") + LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP(1549), + + @UiEvent(doc = "User tapped private space settings button") + LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP(1550), // ADD MORE ; diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index f4ce3607f7..a98ec6484a 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -411,7 +411,7 @@ public class LoaderTask implements Runnable { mSessionHelper.getActiveSessions(); installingPkgs.forEach(mApp.getIconCache()::updateSessionCache); FileLog.d(TAG, "loadWorkspace: Packages with active install sessions: " - + installingPkgs.values()); + + installingPkgs.keySet().stream().map(info -> info.mPackageName).toList()); final PackageUserKey tempPackageKey = new PackageUserKey(null, null); mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs); @@ -440,10 +440,15 @@ public class LoaderTask implements Runnable { mShortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut); } + if (pinnedShortcuts.isEmpty()) { + FileLog.d(TAG, "No pinned shortcuts found for user " + user); + } } else { // Shortcut manager can fail due to some race condition when the // lock state changes too frequently. For the purpose of the loading // shortcuts, consider the user is still locked. + FileLog.d(TAG, "Shortcut request failed for user " + + user + ", user may still be locked."); userUnlocked = false; } } @@ -481,16 +486,17 @@ public class LoaderTask implements Runnable { mItemsDeleted = c.commitDeleted(); // Sort the folder items, update ranks, and make sure all preview items are high res. - FolderGridOrganizer verifier = - new FolderGridOrganizer(mApp.getInvariantDeviceProfile()); + List<FolderGridOrganizer> verifiers = + mApp.getInvariantDeviceProfile().supportedProfiles.stream().map( + FolderGridOrganizer::new).toList(); for (FolderInfo folder : mBgDataModel.folders) { Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR); - verifier.setFolderInfo(folder); + verifiers.forEach(verifier -> verifier.setFolderInfo(folder)); int size = folder.contents.size(); // Update ranks here to ensure there are no gaps caused by removed folder items. - // Ranks are the source of truth for folder items, so cellX and cellY can be ignored - // for now. Database will be updated once user manually modifies folder. + // Ranks are the source of truth for folder items, so cellX and cellY can be + // ignored for now. Database will be updated once user manually modifies folder. for (int rank = 0; rank < size; ++rank) { WorkspaceItemInfo info = folder.contents.get(rank); // rank is used differently in app pairs, so don't reset @@ -498,9 +504,9 @@ public class LoaderTask implements Runnable { info.rank = rank; } - if (info.usingLowResIcon() - && info.itemType == Favorites.ITEM_TYPE_APPLICATION - && verifier.isItemInPreview(info.rank)) { + if (info.usingLowResIcon() && info.itemType == Favorites.ITEM_TYPE_APPLICATION + && verifiers.stream().anyMatch( + verifier -> verifier.isItemInPreview(info.rank))) { mIconCache.getTitleAndIcon(info, false); } } @@ -589,17 +595,17 @@ public class LoaderTask implements Runnable { // Package is not yet available but might be // installed later. FileLog.d(TAG, "package not yet restored: " + targetPkg); - tempPackageKey.update(targetPkg, c.user); if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) { // Restore has started once. } else if (installingPkgs.containsKey(tempPackageKey)) { // App restore has started. Update the flag c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED; - c.updater().put(Favorites.RESTORED, - c.restoreFlag).commit(); + FileLog.d(TAG, "restore started for installing app: " + targetPkg); + c.updater().put(Favorites.RESTORED, c.restoreFlag).commit(); } else { - c.markDeleted("Unrestored app removed: " + targetPkg); + c.markDeleted("removing app that is not restored and not " + + "installing. package: " + targetPkg); return; } } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) { @@ -610,7 +616,7 @@ public class LoaderTask implements Runnable { } else if (!isSdCardReady) { // SdCard is not ready yet. Package might get available, // once it is ready. - Log.d(TAG, "Missing pkg, will check later: " + targetPkg); + Log.d(TAG, "Missing package, will check later: " + targetPkg); mPendingPackages.add(new PackageUserKey(targetPkg, c.user)); // Add the icon on the workspace anyway. allowMissingTarget = true; @@ -646,7 +652,8 @@ public class LoaderTask implements Runnable { ShortcutInfo pinnedShortcut = mShortcutKeyToPinnedShortcuts.get(key); if (pinnedShortcut == null) { // The shortcut is no longer valid. - c.markDeleted("Pinned shortcut not found"); + c.markDeleted("Pinned shortcut not found for package: " + + key.getPackageName()); return; } info = new WorkspaceItemInfo(pinnedShortcut, mApp.getContext()); @@ -816,7 +823,7 @@ public class LoaderTask implements Runnable { } else { Log.v(TAG, "Widget restore pending id=" + c.id + " appWidgetId=" + appWidgetId - + " status =" + c.restoreFlag); + + " status=" + c.restoreFlag); appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, component); appWidgetInfo.restoreStatus = c.restoreFlag; diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java index 139efc3d99..d2b7161697 100644 --- a/src/com/android/launcher3/model/ModelDbController.java +++ b/src/com/android/launcher3/model/ModelDbController.java @@ -62,6 +62,7 @@ import com.android.launcher3.LauncherPrefs; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.Utilities; +import com.android.launcher3.logging.FileLog; import com.android.launcher3.pm.UserCache; import com.android.launcher3.provider.LauncherDbUtils; import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; @@ -262,7 +263,7 @@ public class ModelDbController { */ public void tryMigrateDB() { if (!migrateGridIfNeeded()) { - Log.d(TAG, "Migration failed: resetting launcher database"); + FileLog.d(TAG, "Migration failed: resetting launcher database"); createEmptyDB(); LauncherPrefs.get(mContext).putSync( getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()).to(true)); @@ -282,15 +283,17 @@ public class ModelDbController { createDbIfNotExists(); if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey())) { // If we have already create a new DB, ignore migration + Log.d(TAG, "migrateGridIfNeeded: new DB already created, skipping migration"); return false; } InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext); if (!GridSizeMigrationUtil.needsToMigrate(mContext, idp)) { + Log.d(TAG, "migrateGridIfNeeded: no grid migration needed"); return true; } String targetDbName = new DeviceGridState(idp).getDbFile(); if (TextUtils.equals(targetDbName, mOpenHelper.getDatabaseName())) { - Log.e(TAG, "migrateGridIfNeeded - target db is same as current: " + targetDbName); + Log.e(TAG, "migrateGridIfNeeded: target db is same as current: " + targetDbName); return false; } DatabaseHelper oldHelper = mOpenHelper; @@ -299,6 +302,9 @@ public class ModelDbController { try { return GridSizeMigrationUtil.migrateGridIfNeeded(mContext, idp, mOpenHelper, oldHelper.getWritableDatabase()); + } catch (Exception e) { + FileLog.e(TAG, "Failed to migrate grid", e); + return false; } finally { if (mOpenHelper != oldHelper) { oldHelper.close(); diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java index 9bf6d43b17..5b541d045b 100644 --- a/src/com/android/launcher3/model/data/FolderInfo.java +++ b/src/com/android/launcher3/model/data/FolderInfo.java @@ -46,7 +46,6 @@ import java.util.List; import java.util.OptionalInt; import java.util.stream.IntStream; - /** * Represents a folder containing shortcuts or apps. */ diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java index dbd13b38ad..dc8cd3af65 100644 --- a/src/com/android/launcher3/provider/RestoreDbTask.java +++ b/src/com/android/launcher3/provider/RestoreDbTask.java @@ -89,9 +89,9 @@ 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"}; + "container", "cellX", "cellY", "spanX", "spanY", "intent", "appWidgetProvider", + "appWidgetId", "restored"}; /** * Tries to restore the backup DB if needed @@ -141,16 +141,17 @@ public class RestoreDbTask { * 4. If restored from a single display backup, remove gaps between screenIds * 5. Override shortcuts that need to be replaced. * - * @return number of items deleted. + * @return number of items deleted */ @VisibleForTesting protected int sanitizeDB(Context context, ModelDbController controller, SQLiteDatabase db, BackupManager backupManager) throws Exception { - FileLog.d(TAG, "Old Launcher Database before sanitizing:"); + logFavoritesTable(db, "Old Launcher Database before sanitizing:", null, null); // Primary user ids long myProfileId = controller.getSerialNumberForUser(myUserHandle()); long oldProfileId = getDefaultProfileId(db); - FileLog.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); @@ -182,7 +183,7 @@ public class RestoreDbTask { final String[] args = new String[profileIds.length]; Arrays.fill(args, "?"); final String where = "profileId NOT IN (" + TextUtils.join(", ", Arrays.asList(args)) + ")"; - logUnrestoredItems(db, where, profileIds); + logFavoritesTable(db, "items to delete from unrestored profiles:", where, profileIds); int itemsDeletedCount = db.delete(Favorites.TABLE_NAME, where, profileIds); FileLog.d(TAG, itemsDeletedCount + " total items from unrestored user(s) were deleted"); @@ -242,47 +243,6 @@ public class RestoreDbTask { } /** - * 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); - } - } - - /** * Remove gaps between screenIds to make sure no empty pages are left in between. * * e.g. [0, 3, 4, 6, 7] -> [0, 1, 2, 3, 4] @@ -396,7 +356,7 @@ public class RestoreDbTask { IntArray.fromConcatString(lp.get(APP_WIDGET_IDS)).toArray(), host); } else { - FileLog.d(TAG, "No app widget ids to restore."); + FileLog.d(TAG, "No app widget ids were received from backup to restore."); } lp.remove(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS); @@ -409,16 +369,16 @@ public class RestoreDbTask { private void restoreAppWidgetIds(Context context, ModelDbController controller, int[] oldWidgetIds, int[] newWidgetIds, @NonNull AppWidgetHost host) { if (WidgetsModel.GO_DISABLE_WIDGETS) { - Log.e(TAG, "Skipping widget ID remap as widgets not supported"); + FileLog.e(TAG, "Skipping widget ID remap as widgets not supported"); host.deleteHost(); return; } if (!RestoreDbTask.isPending(context)) { // Someone has already gone through our DB once, probably LoaderTask. Skip any further // modifications of the DB. - Log.e(TAG, "Skipping widget ID remap as DB already in use"); + FileLog.e(TAG, "Skipping widget ID remap as DB already in use"); for (int widgetId : newWidgetIds) { - Log.d(TAG, "Deleting widgetId: " + widgetId); + FileLog.d(TAG, "Deleting widgetId: " + widgetId); host.deleteAppWidgetId(widgetId); } return; @@ -426,7 +386,7 @@ public class RestoreDbTask { final AppWidgetManager widgets = AppWidgetManager.getInstance(context); - Log.d(TAG, "restoreAppWidgetIds: " + FileLog.d(TAG, "restoreAppWidgetIds: " + "oldWidgetIds=" + IntArray.wrap(oldWidgetIds).toConcatString() + ", newWidgetIds=" + IntArray.wrap(newWidgetIds).toConcatString()); @@ -434,7 +394,7 @@ public class RestoreDbTask { logDatabaseWidgetInfo(controller); for (int i = 0; i < oldWidgetIds.length; i++) { - Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]); + FileLog.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]); final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]); final int state; @@ -454,7 +414,7 @@ public class RestoreDbTask { final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?"; String profileId = Long.toString(mainProfileId); final String[] args = new String[] { oldWidgetId, profileId }; - Log.d(TAG, "restoreAppWidgetIds: querying profile id=" + profileId + FileLog.d(TAG, "restoreAppWidgetIds: querying profile id=" + profileId + " with controller profile ID=" + controllerProfileId); int result = new ContentWriter(context, new ContentWriter.CommitParams(controller, where, args)) @@ -463,7 +423,7 @@ public class RestoreDbTask { .commit(); if (result == 0) { // TODO(b/234700507): Remove the logs after the bug is fixed - Log.e(TAG, "restoreAppWidgetIds: remapping failed since the widget is not in" + FileLog.e(TAG, "restoreAppWidgetIds: remapping failed since the widget is not in" + " the database anymore"); try (Cursor cursor = controller.getDb().query( Favorites.TABLE_NAME, @@ -471,7 +431,7 @@ public class RestoreDbTask { "appWidgetId=?", new String[]{oldWidgetId}, null, null, null)) { if (!cursor.moveToFirst()) { // The widget no long exists. - Log.d(TAG, "Deleting widgetId: " + newWidgetIds[i] + " with old id: " + FileLog.d(TAG, "Deleting widgetId: " + newWidgetIds[i] + " with old id: " + oldWidgetId); host.deleteAppWidgetId(newWidgetIds[i]); } @@ -523,7 +483,7 @@ public class RestoreDbTask { } builder.append("]"); Log.d(TAG, "restoreAppWidgetIds: all widget ids in database: " - + builder.toString()); + + builder); } catch (Exception ex) { Log.e(TAG, "Getting widget ids from the database failed", ex); } @@ -572,4 +532,45 @@ public class RestoreDbTask { Collectors.joining(" OR ")); } + /** + * Queries and logs the items from the Favorites table 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 logHeader First line in log statement, used to explain what is being logged. + * @param where The SELECT statement to query items. + * @param profileIds The profile ID's for each user profile. + */ + public static void logFavoritesTable(SQLiteDatabase database, @NonNull String logHeader, + 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(logHeader + "\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, "logFavoritesTable: No items found from query for" + + "\"" + logHeader + "\""); + } + } catch (Exception e) { + FileLog.e(TAG, "logFavoritesTable: Error reading from database", e); + } + } } diff --git a/src/com/android/launcher3/responsive/HotseatSpecsProvider.kt b/src/com/android/launcher3/responsive/HotseatSpecsProvider.kt index ebbff51996..7502a43ff9 100644 --- a/src/com/android/launcher3/responsive/HotseatSpecsProvider.kt +++ b/src/com/android/launcher3/responsive/HotseatSpecsProvider.kt @@ -40,13 +40,28 @@ class HotseatSpecsProvider(groupOfSpecs: List<ResponsiveSpecGroup<HotseatSpec>>) return specsGroup } + private fun getSpecIgnoringDimensionType( + availableSize: Int, + specsGroup: ResponsiveSpecGroup<HotseatSpec> + ): HotseatSpec? { + val specWidth = specsGroup.widthSpecs.firstOrNull { availableSize <= it.maxAvailableSize } + val specHeight = specsGroup.heightSpecs.firstOrNull { availableSize <= it.maxAvailableSize } + return specWidth ?: specHeight + } + fun getCalculatedSpec( aspectRatio: Float, dimensionType: DimensionType, - availableSpace: Int + availableSpace: Int, ): CalculatedHotseatSpec { val specsGroup = getSpecsByAspectRatio(aspectRatio) - val spec = specsGroup.getSpec(dimensionType, availableSpace) + + // TODO(b/315548992): Ignore the dimension type to prevent crash before launcher + // data migration is finished. The restore process allows the initialization of + // an invalid or disabled grid until the data is restored and migrated. + val spec = getSpecIgnoringDimensionType(availableSpace, specsGroup) + check(spec != null) { "No available spec found within $availableSpace. $specsGroup" } + // val spec = specsGroup.getSpec(dimensionType, availableSpace) return CalculatedHotseatSpec(availableSpace, spec) } diff --git a/src/com/android/launcher3/responsive/ResponsiveSpecGroup.kt b/src/com/android/launcher3/responsive/ResponsiveSpecGroup.kt index b233d7c3d5..a758be8e1c 100644 --- a/src/com/android/launcher3/responsive/ResponsiveSpecGroup.kt +++ b/src/com/android/launcher3/responsive/ResponsiveSpecGroup.kt @@ -18,6 +18,7 @@ package com.android.launcher3.responsive import android.content.res.TypedArray import com.android.launcher3.R +import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType import com.android.launcher3.responsive.ResponsiveSpec.DimensionType /** @@ -54,10 +55,29 @@ class ResponsiveSpecGroup<T : IResponsiveSpec>( } else { heightSpecs.firstOrNull { availableSize <= it.maxAvailableSize } } - check(spec != null) { "No available $type spec found within $availableSize." } + check(spec != null) { "No available $type spec found within $availableSize. $this" } return spec } + override fun toString(): String { + fun printSpec(spec: IResponsiveSpec) = + when (spec.specType) { + ResponsiveSpecType.AllApps, + ResponsiveSpecType.Folder, + ResponsiveSpecType.Workspace -> (spec as ResponsiveSpec).toString() + ResponsiveSpecType.Hotseat -> (spec as HotseatSpec).toString() + ResponsiveSpecType.Cell -> (spec as CellSpec).toString() + } + + val widthSpecsString = widthSpecs.joinToString(", ") { printSpec(it) } + val heightSpecsString = heightSpecs.joinToString(", ") { printSpec(it) } + return "ResponsiveSpecGroup(" + + "aspectRatio=${aspectRatio}, " + + "widthSpecs=[${widthSpecsString}], " + + "heightSpecs=[${heightSpecsString}]" + + ")" + } + companion object { const val XML_GROUP_NAME = "specs" diff --git a/src/com/android/launcher3/shortcuts/ShortcutRequest.java b/src/com/android/launcher3/shortcuts/ShortcutRequest.java index 5291ce4620..21efceb0fd 100644 --- a/src/com/android/launcher3/shortcuts/ShortcutRequest.java +++ b/src/com/android/launcher3/shortcuts/ShortcutRequest.java @@ -24,10 +24,11 @@ import android.content.pm.LauncherApps; import android.content.pm.LauncherApps.ShortcutQuery; import android.content.pm.ShortcutInfo; import android.os.UserHandle; -import android.util.Log; import androidx.annotation.Nullable; +import com.android.launcher3.logging.FileLog; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -101,7 +102,7 @@ public class ShortcutRequest { return new QueryResult(mContext.getSystemService(LauncherApps.class) .getShortcuts(mQuery, mUserHandle)); } catch (SecurityException | IllegalStateException e) { - Log.e(TAG, "Failed to query for shortcuts", e); + FileLog.e(TAG, "Failed to query for shortcuts", e); return QueryResult.DEFAULT; } } diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index 1b1d347d81..2c834bdb3f 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -47,6 +47,7 @@ import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.testing.shared.HotseatCellCenterRequest; import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.testing.shared.WorkspaceCellCenterRequest; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.ResourceBasedOverride; import com.android.launcher3.widget.picker.WidgetsFullSheet; @@ -179,6 +180,11 @@ public class TestInformationHandler implements ResourceBasedOverride { mDeviceProfile.numShownAllAppsColumns); return response; + case TestProtocol.REQUEST_IS_TRANSIENT_TASKBAR: + response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, + DisplayController.isTransientTaskbar(mContext)); + return response; + case TestProtocol.REQUEST_IS_TWO_PANELS: response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, FOLDABLE_SINGLE_PAGE.get() ? false : mDeviceProfile.isTwoPanels); diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java index 3bce377656..a9c2a2e368 100644 --- a/src/com/android/launcher3/touch/ItemClickHandler.java +++ b/src/com/android/launcher3/touch/ItemClickHandler.java @@ -145,8 +145,8 @@ public class ItemClickHandler { */ 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)); + AppPairIcon appPairIcon = (AppPairIcon) v; + launcher.launchAppPair(appPairIcon); } /** diff --git a/src/com/android/launcher3/util/DimensionUtils.kt b/src/com/android/launcher3/util/DimensionUtils.kt index 0eb0e087ee..63e919ad3e 100644 --- a/src/com/android/launcher3/util/DimensionUtils.kt +++ b/src/com/android/launcher3/util/DimensionUtils.kt @@ -51,12 +51,12 @@ object DimensionUtils { // Taskbar on phone, portrait if (!deviceProfile.isLandscape) { p.x = ViewGroup.LayoutParams.MATCH_PARENT - p.y = res.getDimensionPixelSize(R.dimen.taskbar_size) + p.y = res.getDimensionPixelSize(R.dimen.taskbar_phone_size) return p } // Taskbar on phone, landscape - p.x = res.getDimensionPixelSize(R.dimen.taskbar_size) + p.x = res.getDimensionPixelSize(R.dimen.taskbar_phone_size) p.y = ViewGroup.LayoutParams.MATCH_PARENT return p } diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java index 4c8366830f..18f583d702 100644 --- a/src/com/android/launcher3/util/DisplayController.java +++ b/src/com/android/launcher3/util/DisplayController.java @@ -22,7 +22,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING; import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_KEY; import static com.android.launcher3.Utilities.dpiFromPx; -import static com.android.launcher3.config.FeatureFlags.ENABLE_TRANSIENT_TASKBAR; import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.FlagDebugUtils.appendFlag; @@ -71,7 +70,7 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { private static final String TAG = "DisplayController"; private static final boolean DEBUG = false; - private static boolean sTransientTaskbarStatusForTests; + private static boolean sTransientTaskbarStatusForTests = true; // TODO(b/254119092) remove all logs with this tag public static final String TASKBAR_NOT_DESTROYED_TAG = "b/254119092"; @@ -410,18 +409,19 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { * Returns whether taskbar is transient. */ public boolean isTransientTaskbar() { - // TODO(b/258604917): Once ENABLE_TASKBAR_PINNING is enabled, remove usage of - // sTransientTaskbarStatusForTests and update test to directly - // toggle shred preference to switch transient taskbar on/of - if (!Utilities.isRunningInTestHarness() - && enableTaskbarPinning() - && mIsTaskbarPinned) { + if (navigationMode != NavigationMode.NO_BUTTON) { return false; } - return navigationMode == NavigationMode.NO_BUTTON - && (Utilities.isRunningInTestHarness() - ? sTransientTaskbarStatusForTests - : ENABLE_TRANSIENT_TASKBAR.get() && !mIsTaskbarPinned); + if (Utilities.isRunningInTestHarness()) { + // TODO(b/258604917): Once ENABLE_TASKBAR_PINNING is enabled, remove usage of + // sTransientTaskbarStatusForTests and update test to directly + // toggle shared preference to switch transient taskbar on/off. + return sTransientTaskbarStatusForTests; + } + if (enableTaskbarPinning()) { + return !mIsTaskbarPinned; + } + return true; } /** @@ -485,6 +485,7 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { pw.println(" currentSize=" + info.currentSize); info.mPerDisplayBounds.forEach((key, value) -> pw.println( " perDisplayBounds - " + key + ": " + value)); + pw.println(" isTransientTaskbar=" + info.isTransientTaskbar()); } /** diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java index e32fec2a40..4f20bbcf44 100644 --- a/src/com/android/launcher3/util/VibratorWrapper.java +++ b/src/com/android/launcher3/util/VibratorWrapper.java @@ -62,7 +62,7 @@ public class VibratorWrapper { public static final VibrationEffect EFFECT_CLICK = createPredefined(VibrationEffect.EFFECT_CLICK); - private static final float LOW_TICK_SCALE = 0.7f; + private static final float LOW_TICK_SCALE = 0.9f; private static final float DRAG_TEXTURE_SCALE = 0.03f; private static final float DRAG_COMMIT_SCALE = 0.5f; private static final float DRAG_BUMP_SCALE = 0.4f; @@ -240,7 +240,7 @@ public class VibratorWrapper { /** Indicates that search has been invoked. */ public void vibrateForSearch() { - if (mSearchEffect != null && FeatureFlags.ENABLE_SEARCH_HAPTIC_COMMIT.get()) { + if (mSearchEffect != null) { vibrate(mSearchEffect); } } diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java index e4df413489..a1cd697eee 100644 --- a/src/com/android/launcher3/views/BaseDragLayer.java +++ b/src/com/android/launcher3/views/BaseDragLayer.java @@ -41,6 +41,7 @@ import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.Utilities; +import com.android.launcher3.testing.shared.ResourceUtils; import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.TouchController; @@ -559,7 +560,8 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext> DeviceProfile dp = mActivity.getDeviceProfile(); if (dp.isTaskbarPresent) { // Ignore taskbar gesture insets to avoid interfering with TouchControllers. - gestureInsetBottom = Math.max(0, gestureInsetBottom - dp.taskbarHeight); + gestureInsetBottom = ResourceUtils.getNavbarSize( + ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, getResources()); } mSystemGestureRegion.set( Math.max(gestureInsets.left, imeInset.left), diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index cbc85b60d8..5cdad03116 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -166,12 +166,16 @@ public class OptionsPopupView<T extends Context & ActivityContext> extends Arrow return show(activityContext, targetRect, items, shouldAddArrow, 0 /* width */); } - public static <T extends Context & ActivityContext> OptionsPopupView<T> show( - ActivityContext activityContext, + @Nullable + private static <T extends Context & ActivityContext> OptionsPopupView<T> show( + @Nullable ActivityContext activityContext, RectF targetRect, List<OptionItem> items, boolean shouldAddArrow, int width) { + if (activityContext == null) { + return null; + } OptionsPopupView<T> popup = (OptionsPopupView<T>) activityContext.getLayoutInflater() .inflate(R.layout.longpress_options_menu, activityContext.getDragLayer(), false); popup.mTargetRect = targetRect; diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java index 5ec1022a62..5171fa218a 100644 --- a/src/com/android/launcher3/widget/BaseWidgetSheet.java +++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java @@ -16,6 +16,7 @@ package com.android.launcher3.widget; import static com.android.app.animation.Interpolators.EMPHASIZED; +import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker; import static com.android.launcher3.LauncherPrefs.WIDGETS_EDUCATION_TIP_SEEN; import android.content.Context; @@ -67,6 +68,8 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity> protected int mNavBarScrimHeight; private final Paint mNavBarScrimPaint; + private boolean mDisableNavBarScrim = false; + public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mContentHorizontalMargin = getResources().getDimensionPixelSize( @@ -105,6 +108,7 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity> mNavBarScrimPaint.setColor(navBarScrimColor); invalidate(); } + setupNavBarColor(); } @Override @@ -146,8 +150,15 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity> } } + /** Enables or disables the sheet's nav bar scrim. */ + public void disableNavBarScrim(boolean disable) { + mDisableNavBarScrim = disable; + } + private int getNavBarScrimHeight(WindowInsets insets) { - if (Utilities.ATLEAST_Q) { + if (mDisableNavBarScrim) { + return 0; + } else if (Utilities.ATLEAST_Q) { return insets.getTappableElementInsets().bottom; } else { return insets.getStableInsetBottom(); @@ -181,12 +192,8 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity> DeviceProfile deviceProfile = mActivityContext.getDeviceProfile(); int widthUsed; if (deviceProfile.isTablet) { - int margin = deviceProfile.allAppsLeftRightMargin; - if (deviceProfile.isLandscape && !deviceProfile.isTwoPanels) { - margin = getResources().getDimensionPixelSize( - R.dimen.widget_picker_landscape_tablet_left_right_margin); - } - widthUsed = Math.max(2 * margin, 2 * (mInsets.left + mInsets.right)); + widthUsed = Math.max(2 * getTabletMargin(deviceProfile), + 2 * (mInsets.left + mInsets.right)); } else if (mInsets.bottom > 0) { widthUsed = mInsets.left + mInsets.right; } else { @@ -201,6 +208,18 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity> MeasureSpec.getSize(heightMeasureSpec)); } + private int getTabletMargin(DeviceProfile deviceProfile) { + if (deviceProfile.isLandscape && !deviceProfile.isTwoPanels) { + return getResources().getDimensionPixelSize( + R.dimen.widget_picker_landscape_tablet_left_right_margin); + } + if (deviceProfile.isTwoPanels && enableUnfoldedTwoPanePicker()) { + return getResources().getDimensionPixelSize( + R.dimen.widget_picker_two_panels_left_right_margin); + } + return deviceProfile.allAppsLeftRightMargin; + } + @Override protected Interpolator getIdleInterpolator() { return mActivityContext.getDeviceProfile().isTablet @@ -218,10 +237,18 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity> } protected void setupNavBarColor() { - boolean isSheetDark = Themes.getAttrBoolean(getContext(), R.attr.isMainColorDark); - getSystemUiController().updateUiState( - SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, - isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV); + boolean isNavBarDark = Themes.getAttrBoolean(getContext(), R.attr.isMainColorDark); + + // In light mode, landscape reverses navbar background color. + boolean isPhoneLandscape = + !mActivityContext.getDeviceProfile().isTablet && mInsets.bottom == 0; + if (!isNavBarDark && isPhoneLandscape) { + isNavBarDark = true; + } + + getSystemUiController().updateUiState(SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, + isNavBarDark ? SystemUiController.FLAG_DARK_NAV + : SystemUiController.FLAG_LIGHT_NAV); } protected SystemUiController getSystemUiController() { diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index da90f176aa..e9a590b814 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -17,6 +17,7 @@ package com.android.launcher3.widget.picker; import static android.view.View.MeasureSpec.makeMeasureSpec; +import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.LauncherPrefs.WIDGETS_EDUCATION_DIALOG_SEEN; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED; @@ -62,7 +63,6 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.compat.AccessibilityManagerCompat; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.UserManagerState; import com.android.launcher3.model.WidgetItem; import com.android.launcher3.pm.UserCache; @@ -163,9 +163,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet private boolean mIsInSearchMode; private boolean mIsNoWidgetsViewNeeded; @Px private int mMaxSpanPerRow; - private final DeviceProfile mDeviceProfile; - - private int mOrientation; + protected DeviceProfile mDeviceProfile; protected TextView mNoWidgetsView; protected StickyHeaderLayout mSearchScrollView; @@ -179,7 +177,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mDeviceProfile = mActivityContext.getDeviceProfile(); - mOrientation = context.getResources().getConfiguration().orientation; mUserCache = UserCache.INSTANCE.get(context); mHasWorkProfile = mUserCache.getUserProfiles() .stream() @@ -676,30 +673,26 @@ public class WidgetsFullSheet extends BaseWidgetSheet /** Shows the {@link WidgetsFullSheet} on the launcher. */ public static WidgetsFullSheet show(BaseActivity activity, boolean animate) { - boolean isTwoPane = activity.getDeviceProfile().isTablet - && activity.getDeviceProfile().isLandscape - && (!activity.getDeviceProfile().isTwoPanels - || FeatureFlags.UNFOLDED_WIDGET_PICKER.get()); - - WidgetsFullSheet sheet; - if (isTwoPane) { - sheet = (WidgetsTwoPaneSheet) activity.getLayoutInflater().inflate( - R.layout.widgets_two_pane_sheet, - activity.getDragLayer(), - false); - } else { - sheet = (WidgetsFullSheet) activity.getLayoutInflater().inflate( - R.layout.widgets_full_sheet, - activity.getDragLayer(), - false); - } - + WidgetsFullSheet sheet = (WidgetsFullSheet) activity.getLayoutInflater().inflate( + getWidgetSheetId(activity), + activity.getDragLayer(), + false); sheet.attachToContainer(); sheet.mIsOpen = true; sheet.open(animate); return sheet; } + private static int getWidgetSheetId(BaseActivity activity) { + boolean isTwoPane = (activity.getDeviceProfile().isTablet + && activity.getDeviceProfile().isLandscape + && !activity.getDeviceProfile().isTwoPanels) + // Enables two pane picker for unfolded foldables if the flag is on. + || (activity.getDeviceProfile().isTwoPanels && enableUnfoldedTwoPanePicker()); + + return isTwoPane ? R.layout.widgets_two_pane_sheet : R.layout.widgets_full_sheet; + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return isTouchOnScrollbar(ev) || super.onInterceptTouchEvent(ev); @@ -790,17 +783,25 @@ public class WidgetsFullSheet extends BaseWidgetSheet if (mIsInSearchMode) { mSearchBar.reset(); } + } - // Checks the orientation of the screen - if (mOrientation != newConfig.orientation) { - mOrientation = newConfig.orientation; - if (mDeviceProfile.isTablet && !mDeviceProfile.isTwoPanels) { - handleClose(false); - show(BaseActivity.fromContext(getContext()), false); - } else { - reset(); - } + @Override + public void onDeviceProfileChanged(DeviceProfile dp) { + if (mDeviceProfile.isLandscape != dp.isLandscape && dp.isTablet && !dp.isTwoPanels) { + handleClose(false); + show(BaseActivity.fromContext(getContext()), false); + } else { + reset(); + } + + // When folding/unfolding the foldables, we need to switch between the regular widget picker + // and the two pane picker, so we rebuild the picker with the correct layout. + if (mDeviceProfile.isTwoPanels != dp.isTwoPanels && enableUnfoldedTwoPanePicker()) { + handleClose(false); + show(BaseActivity.fromContext(getContext()), false); } + + mDeviceProfile = dp; } @Override diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java index d85737b28f..c3ab08c153 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.widget.picker; +import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker; + import android.content.Context; import android.graphics.Outline; import android.os.Process; @@ -23,12 +25,15 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewOutlineProvider; +import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ScrollView; import androidx.annotation.NonNull; +import androidx.annotation.Px; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.recyclerview.ViewHolderBinder; import com.android.launcher3.util.PackageUserKey; @@ -46,6 +51,8 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet { private static final int PERSONAL_TAB = 0; private static final int WORK_TAB = 1; + private static final int MINIMUM_WIDTH_LEFT_PANE_FOLDABLE_DP = 268; + private static final int MAXIMUM_WIDTH_LEFT_PANE_FOLDABLE_DP = 395; private static final String SUGGESTIONS_PACKAGE_NAME = "widgets_list_suggestions_entry"; private LinearLayout mSuggestedWidgetsContainer; @@ -117,6 +124,29 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet { } @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + if (changed && mDeviceProfile.isTwoPanels && enableUnfoldedTwoPanePicker()) { + LinearLayout layout = mContent.findViewById(R.id.linear_layout_container); + FrameLayout leftPane = layout.findViewById(R.id.recycler_view_container); + LinearLayout.LayoutParams layoutParams = (LayoutParams) leftPane.getLayoutParams(); + // Width is 1/3 of the sheet unless it's less than min width or max width + int leftPaneWidth = layout.getMeasuredWidth() / 3; + @Px int minLeftPaneWidthPx = Utilities.dpToPx(MINIMUM_WIDTH_LEFT_PANE_FOLDABLE_DP); + @Px int maxLeftPaneWidthPx = Utilities.dpToPx(MAXIMUM_WIDTH_LEFT_PANE_FOLDABLE_DP); + if (leftPaneWidth < minLeftPaneWidthPx) { + layoutParams.width = minLeftPaneWidthPx; + } else if (leftPaneWidth > maxLeftPaneWidthPx) { + layoutParams.width = maxLeftPaneWidthPx; + } else { + layoutParams.width = 0; + } + layoutParams.weight = layoutParams.width == 0 ? 0.33F : 0; + leftPane.setLayoutParams(layoutParams); + } + } + + @Override public void onRecommendedWidgetsBound() { super.onRecommendedWidgetsBound(); diff --git a/tests/assets/ReorderWidgets/full_reorder_case b/tests/assets/ReorderWidgets/full_reorder_case index 33ebaaea28..850e4fdd0e 100644 --- a/tests/assets/ReorderWidgets/full_reorder_case +++ b/tests/assets/ReorderWidgets/full_reorder_case @@ -20,7 +20,7 @@ xxxx bbmm iimm iiaa -arguments: 0 3 +arguments: 0 2 board: 4x4 xxxx bbii diff --git a/tests/assets/ReorderWidgets/simple_reorder_case b/tests/assets/ReorderWidgets/simple_reorder_case index f5eb7b6f5c..2c50ce4d68 100644 --- a/tests/assets/ReorderWidgets/simple_reorder_case +++ b/tests/assets/ReorderWidgets/simple_reorder_case @@ -34,7 +34,7 @@ xxxx --mm --mm ---- -arguments: 3 3 +arguments: 2 2 board: 4x4 xxxx ---- diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt index 5f5cf5e897..18752e9f0d 100644 --- a/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt +++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt @@ -38,8 +38,8 @@ DeviceProfile: iconSizePx: 147.0px (56.0dp) iconTextSizePx: 38.0px (14.476191dp) iconDrawablePaddingPx: 12.0px (4.571429dp) - inv.numFolderRows: 4 - inv.numFolderColumns: 4 + numFolderRows: 4 + numFolderColumns: 4 folderCellWidthPx: 195.0px (74.28571dp) folderCellHeightPx: 230.0px (87.61905dp) folderChildIconSizePx: 147.0px (56.0dp) diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt index 02bab0e7ac..c0de8d8594 100644 --- a/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt +++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt @@ -38,8 +38,8 @@ DeviceProfile: iconSizePx: 147.0px (56.0dp) iconTextSizePx: 38.0px (14.476191dp) iconDrawablePaddingPx: 12.0px (4.571429dp) - inv.numFolderRows: 4 - inv.numFolderColumns: 4 + numFolderRows: 4 + numFolderColumns: 4 folderCellWidthPx: 195.0px (74.28571dp) folderCellHeightPx: 230.0px (87.61905dp) folderChildIconSizePx: 147.0px (56.0dp) diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt index 1ade7799a2..920ba6f49b 100644 --- a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt +++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt @@ -38,8 +38,8 @@ DeviceProfile: iconSizePx: 147.0px (56.0dp) iconTextSizePx: 0.0px (0.0dp) iconDrawablePaddingPx: 0.0px (0.0dp) - inv.numFolderRows: 4 - inv.numFolderColumns: 4 + numFolderRows: 4 + numFolderColumns: 4 folderCellWidthPx: 163.0px (62.095238dp) folderCellHeightPx: 192.0px (73.14286dp) folderChildIconSizePx: 123.0px (46.857143dp) diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt index b0b745daf0..65460eca1a 100644 --- a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt +++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt @@ -38,8 +38,8 @@ DeviceProfile: iconSizePx: 147.0px (56.0dp) iconTextSizePx: 0.0px (0.0dp) iconDrawablePaddingPx: 0.0px (0.0dp) - inv.numFolderRows: 4 - inv.numFolderColumns: 4 + numFolderRows: 4 + numFolderColumns: 4 folderCellWidthPx: 173.0px (65.90476dp) folderCellHeightPx: 205.0px (78.09524dp) folderChildIconSizePx: 131.0px (49.904762dp) diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt index d7f3c1a624..1781673128 100644 --- a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt +++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt @@ -38,8 +38,8 @@ DeviceProfile: iconSizePx: 120.0px (60.0dp) iconTextSizePx: 28.0px (14.0dp) iconDrawablePaddingPx: 9.0px (4.5dp) - inv.numFolderRows: 3 - inv.numFolderColumns: 3 + numFolderRows: 3 + numFolderColumns: 3 folderCellWidthPx: 240.0px (120.0dp) folderCellHeightPx: 208.0px (104.0dp) folderChildIconSizePx: 120.0px (60.0dp) diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt index 20a2a99c68..bd9e2675a4 100644 --- a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt +++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt @@ -38,8 +38,8 @@ DeviceProfile: iconSizePx: 120.0px (60.0dp) iconTextSizePx: 28.0px (14.0dp) iconDrawablePaddingPx: 9.0px (4.5dp) - inv.numFolderRows: 3 - inv.numFolderColumns: 3 + numFolderRows: 3 + numFolderColumns: 3 folderCellWidthPx: 240.0px (120.0dp) folderCellHeightPx: 208.0px (104.0dp) folderChildIconSizePx: 120.0px (60.0dp) diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt index 94022e4a86..e983ef76fc 100644 --- a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt +++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt @@ -38,8 +38,8 @@ DeviceProfile: iconSizePx: 120.0px (60.0dp) iconTextSizePx: 28.0px (14.0dp) iconDrawablePaddingPx: 9.0px (4.5dp) - inv.numFolderRows: 3 - inv.numFolderColumns: 3 + numFolderRows: 3 + numFolderColumns: 3 folderCellWidthPx: 204.0px (102.0dp) folderCellHeightPx: 240.0px (120.0dp) folderChildIconSizePx: 120.0px (60.0dp) diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt index 79772043dc..aa92838ff9 100644 --- a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt +++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt @@ -38,8 +38,8 @@ DeviceProfile: iconSizePx: 120.0px (60.0dp) iconTextSizePx: 28.0px (14.0dp) iconDrawablePaddingPx: 9.0px (4.5dp) - inv.numFolderRows: 3 - inv.numFolderColumns: 3 + numFolderRows: 3 + numFolderColumns: 3 folderCellWidthPx: 204.0px (102.0dp) folderCellHeightPx: 240.0px (120.0dp) folderChildIconSizePx: 120.0px (60.0dp) diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt index 0b179967f7..43e4a604e6 100644 --- a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt +++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt @@ -38,8 +38,8 @@ DeviceProfile: iconSizePx: 141.0px (53.714287dp) iconTextSizePx: 34.0px (12.952381dp) iconDrawablePaddingPx: 13.0px (4.952381dp) - inv.numFolderRows: 3 - inv.numFolderColumns: 4 + numFolderRows: 3 + numFolderColumns: 4 folderCellWidthPx: 189.0px (72.0dp) folderCellHeightPx: 219.0px (83.42857dp) folderChildIconSizePx: 141.0px (53.714287dp) diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt index 71fffe8b38..e7ea83982b 100644 --- a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt +++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt @@ -38,8 +38,8 @@ DeviceProfile: iconSizePx: 141.0px (53.714287dp) iconTextSizePx: 34.0px (12.952381dp) iconDrawablePaddingPx: 13.0px (4.952381dp) - inv.numFolderRows: 3 - inv.numFolderColumns: 4 + numFolderRows: 3 + numFolderColumns: 4 folderCellWidthPx: 189.0px (72.0dp) folderCellHeightPx: 219.0px (83.42857dp) folderChildIconSizePx: 141.0px (53.714287dp) diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt index 5da4ed0900..043380c607 100644 --- a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt +++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt @@ -38,8 +38,8 @@ DeviceProfile: iconSizePx: 141.0px (53.714287dp) iconTextSizePx: 34.0px (12.952381dp) iconDrawablePaddingPx: 13.0px (4.952381dp) - inv.numFolderRows: 3 - inv.numFolderColumns: 4 + numFolderRows: 3 + numFolderColumns: 4 folderCellWidthPx: 189.0px (72.0dp) folderCellHeightPx: 219.0px (83.42857dp) folderChildIconSizePx: 141.0px (53.714287dp) diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt index 359e530102..a1b3e957e7 100644 --- a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt +++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt @@ -38,8 +38,8 @@ DeviceProfile: iconSizePx: 141.0px (53.714287dp) iconTextSizePx: 34.0px (12.952381dp) iconDrawablePaddingPx: 13.0px (4.952381dp) - inv.numFolderRows: 3 - inv.numFolderColumns: 4 + numFolderRows: 3 + numFolderColumns: 4 folderCellWidthPx: 189.0px (72.0dp) folderCellHeightPx: 219.0px (83.42857dp) folderChildIconSizePx: 141.0px (53.714287dp) diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java index d5a645ee46..fcb5158165 100644 --- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java +++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java @@ -30,6 +30,8 @@ public final class TestProtocol { public static final String FOLDER_OPENED_MESSAGE = "TAPL_FOLDER_OPENED"; public static final String SEARCH_RESULT_COMPLETE = "SEARCH_RESULT_COMPLETE"; public static final String LAUNCHER_ACTIVITY_STOPPED_MESSAGE = "TAPL_LAUNCHER_ACTIVITY_STOPPED"; + public static final String WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE = + "TAPL_WALLPAPER_OPEN_ANIMATION_FINISHED"; public static final int NORMAL_STATE_ORDINAL = 0; public static final int SPRING_LOADED_STATE_ORDINAL = 1; public static final int OVERVIEW_STATE_ORDINAL = 2; @@ -90,14 +92,13 @@ public final class TestProtocol { "is-launcher-activity-started"; public static final String REQUEST_FREEZE_APP_LIST = "freeze-app-list"; public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list"; - public static final String REQUEST_ENABLE_MANUAL_TASKBAR_STASHING = "enable-taskbar-stashing"; - public static final String REQUEST_DISABLE_MANUAL_TASKBAR_STASHING = "disable-taskbar-stashing"; public static final String REQUEST_ENABLE_BLOCK_TIMEOUT = "enable-block-timeout"; public static final String REQUEST_DISABLE_BLOCK_TIMEOUT = "disable-block-timeout"; public static final String REQUEST_ENABLE_TRANSIENT_TASKBAR = "enable-transient-taskbar"; public static final String REQUEST_DISABLE_TRANSIENT_TASKBAR = "disable-transient-taskbar"; + public static final String REQUEST_IS_TRANSIENT_TASKBAR = "is-transient-taskbar"; public static final String REQUEST_UNSTASH_TASKBAR_IF_STASHED = "unstash-taskbar-if-stashed"; - public static final String REQUEST_STASHED_TASKBAR_HEIGHT = "stashed-taskbar-height"; + public static final String REQUEST_TASKBAR_FROM_NAV_THRESHOLD = "taskbar-from-nav-threshold"; public static final String REQUEST_STASHED_TASKBAR_SCALE = "taskbar-stash-handle-scale"; public static final String REQUEST_RECREATE_TASKBAR = "recreate-taskbar"; public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags"; diff --git a/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt b/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt index a42100626a..30b5663a1e 100644 --- a/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt +++ b/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt @@ -121,8 +121,8 @@ abstract class FakeInvariantDeviceProfileTest { listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f)) .toTypedArray() - numFolderRows = 3 - numFolderColumns = 3 + numFolderRows = intArrayOf(3, 3, 3, 3) + numFolderColumns = intArrayOf(3, 3, 3, 3) folderStyle = R.style.FolderStyleDefault inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split @@ -204,8 +204,8 @@ abstract class FakeInvariantDeviceProfileTest { listOf(PointF(16f, 64f), PointF(64f, 16f), PointF(16f, 64f), PointF(16f, 64f)) .toTypedArray() - numFolderRows = 3 - numFolderColumns = 3 + numFolderRows = intArrayOf(3, 3, 3, 3) + numFolderColumns = intArrayOf(3, 3, 3, 3) folderStyle = R.style.FolderStyleDefault inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_6_5 @@ -288,8 +288,8 @@ abstract class FakeInvariantDeviceProfileTest { listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 20f), PointF(20f, 20f)) .toTypedArray() - numFolderRows = 3 - numFolderColumns = 3 + numFolderRows = intArrayOf(3, 3, 3, 3) + numFolderColumns = intArrayOf(3, 3, 3, 3) folderStyle = R.style.FolderStyleDefault inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split diff --git a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java index 4a42887b13..b4a5169c30 100644 --- a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java +++ b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java @@ -208,9 +208,10 @@ public class TaplOpenCloseAllApps extends AbstractLauncherUiTest { InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( READ_DEVICE_CONFIG_PERMISSION); assumeFalse(FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get()); - mLauncher.getWorkspace().switchToAllApps(); - mLauncher.pressBack(); - mLauncher.getWorkspace(); + mLauncher + .getWorkspace() + .switchToAllApps() + .pressBackToWorkspace(); waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); mLauncher.pressBack(); diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java index 28471f6a3f..e1af7746fc 100644 --- a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java +++ b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java @@ -191,9 +191,21 @@ public class ReorderAlgorithmUnitTest { int[] testCaseXYinPixels = new int[2]; cl.regionToCenterPoint(x, y, spanX, spanY, testCaseXYinPixels); - ItemConfiguration solution = cl.createReorderAlgorithm().calculateReorder( - testCaseXYinPixels[0], testCaseXYinPixels[1], minSpanX, minSpanY, spanX, spanY, - null); + ItemConfiguration configuration = new ItemConfiguration(); + cl.copyCurrentStateToSolution(configuration); + ItemConfiguration solution = cl.createReorderAlgorithm() + .calculateReorder( + new ReorderParameters( + testCaseXYinPixels[0], + testCaseXYinPixels[1], + spanX, + spanY, + minSpanX, + minSpanY, + null, + configuration + ) + ); if (solution == null) { solution = new ItemConfiguration(); solution.isSolution = false; diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/TaplReorderWidgetsTest.java index 30bde0a8c3..8bdcd033f7 100644 --- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java +++ b/tests/src/com/android/launcher3/celllayout/TaplReorderWidgetsTest.java @@ -58,12 +58,12 @@ import java.util.Map; @SmallTest @RunWith(AndroidJUnit4.class) -public class ReorderWidgets extends AbstractLauncherUiTest { +public class TaplReorderWidgetsTest extends AbstractLauncherUiTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); - private static final String TAG = ReorderWidgets.class.getSimpleName(); + private static final String TAG = TaplReorderWidgetsTest.class.getSimpleName(); private static final List<String> FOLDABLE_GRIDS = List.of("normal", "practical", "reasonable"); diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java index c5d5de8a39..8200c9443c 100644 --- a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java +++ b/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java @@ -41,7 +41,7 @@ import java.util.UUID; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class PromiseIconUiTest extends AbstractLauncherUiTest { +public class TaplPromiseIconUiTest extends AbstractLauncherUiTest { private int mSessionId = -1; diff --git a/tests/src/com/android/launcher3/dragging/TaplUninstallRemove.java b/tests/src/com/android/launcher3/dragging/TaplUninstallRemove.java index ed343073fe..568fc9f9c3 100644 --- a/tests/src/com/android/launcher3/dragging/TaplUninstallRemove.java +++ b/tests/src/com/android/launcher3/dragging/TaplUninstallRemove.java @@ -166,9 +166,11 @@ public class TaplUninstallRemove extends AbstractLauncherUiTest { mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone( DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME); - Map<String, Point> finalPositions = - mLauncher.getWorkspace().getWorkspaceIconsPositions(); - assertThat(finalPositions).doesNotContainKey(DUMMY_APP_NAME); + if (!TestStabilityRule.isPresubmit()) { // b/315847371 + Map<String, Point> finalPositions = + mLauncher.getWorkspace().getWorkspaceIconsPositions(); + assertThat(finalPositions).doesNotContainKey(DUMMY_APP_NAME); + } } finally { TestUtil.uninstallDummyApp(); } diff --git a/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt b/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt new file mode 100644 index 0000000000..60a4d2d667 --- /dev/null +++ b/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt @@ -0,0 +1,184 @@ +/* + * 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.model + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.launcher3.LauncherAppState +import com.android.launcher3.model.data.WorkspaceItemInfo +import com.android.launcher3.util.Executors +import com.android.launcher3.util.LauncherLayoutBuilder +import com.android.launcher3.util.LauncherModelHelper +import com.android.launcher3.util.LauncherModelHelper.* +import com.android.launcher3.util.TestUtil +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage +import java.util.concurrent.CountDownLatch +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** Tests to verify that folder icons are loaded with appropriate resolution */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class FolderIconLoadTest { + private lateinit var modelHelper: LauncherModelHelper + + private val uniqueActivities = + listOf( + TEST_ACTIVITY, + TEST_ACTIVITY2, + TEST_ACTIVITY3, + TEST_ACTIVITY4, + TEST_ACTIVITY5, + TEST_ACTIVITY6, + TEST_ACTIVITY7, + TEST_ACTIVITY8, + TEST_ACTIVITY9, + TEST_ACTIVITY10, + TEST_ACTIVITY11, + TEST_ACTIVITY12, + TEST_ACTIVITY13, + TEST_ACTIVITY14 + ) + + @Before + fun setUp() { + modelHelper = LauncherModelHelper() + } + + @After + @Throws(Exception::class) + fun tearDown() { + modelHelper.destroy() + TestUtil.uninstallDummyApp() + } + + @Test + @Throws(Exception::class) + fun folderLoadedWithHighRes_2x2() { + val items = setupAndLoadFolder(4) + assertThat(items.size).isEqualTo(4) + verifyHighRes(items, 0, 1, 2, 3) + } + + @Test + @Throws(Exception::class) + fun folderLoadedWithHighRes_3x2() { + val items = setupAndLoadFolder(6) + assertThat(items.size).isEqualTo(6) + verifyHighRes(items, 0, 1, 3, 4) + verifyLowRes(items, 2, 5) + } + + @Test + @Throws(Exception::class) + fun folderLoadedWithHighRes_max_3x3() { + val idp = LauncherAppState.getIDP(modelHelper.sandboxContext) + idp.numFolderColumns = intArrayOf(3, 3, 3, 3) + idp.numFolderRows = intArrayOf(3, 3, 3, 3) + recreateSupportedDeviceProfiles() + + val items = setupAndLoadFolder(14) + verifyHighRes(items, 0, 1, 3, 4) + verifyLowRes(items, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13) + } + + @Test + @Throws(Exception::class) + fun folderLoadedWithHighRes_max_4x4() { + val idp = LauncherAppState.getIDP(modelHelper.sandboxContext) + idp.numFolderColumns = intArrayOf(4, 4, 4, 4) + idp.numFolderRows = intArrayOf(4, 4, 4, 4) + recreateSupportedDeviceProfiles() + + val items = setupAndLoadFolder(14) + verifyHighRes(items, 0, 1, 4, 5) + verifyLowRes(items, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13) + } + + @Test + @Throws(Exception::class) + fun folderLoadedWithHighRes_differentFolderConfigurations() { + val idp = LauncherAppState.getIDP(modelHelper.sandboxContext) + idp.numFolderColumns = intArrayOf(4, 3, 4, 4) + idp.numFolderRows = intArrayOf(4, 3, 4, 4) + recreateSupportedDeviceProfiles() + + val items = setupAndLoadFolder(14) + verifyHighRes(items, 0, 1, 3, 4, 5) + verifyLowRes(items, 2, 6, 7, 8, 9, 10, 11, 12, 13) + } + + @Throws(Exception::class) + private fun setupAndLoadFolder(itemCount: Int): ArrayList<WorkspaceItemInfo> { + val builder = + LauncherLayoutBuilder() + .atWorkspace(0, 0, 1) + .putFolder("Sample") + .apply { + for (i in 0..itemCount - 1) this.addApp(TEST_PACKAGE, uniqueActivities[i]) + } + .build() + + modelHelper.setupDefaultLayoutProvider(builder) + modelHelper.loadModelSync() + + // The first load initializes the DB, load again so that icons are now used from the DB + // Wait for the icon cache to be updated and then reload + val app = LauncherAppState.getInstance(modelHelper.sandboxContext) + val cache = app.iconCache + while (cache.isIconUpdateInProgress) { + val wait = CountDownLatch(1) + Executors.MODEL_EXECUTOR.handler.postDelayed({ wait.countDown() }, 10) + wait.await() + } + TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { cache.clearMemoryCache() } + // Reload again with correct icon state + app.model.forceReload() + modelHelper.loadModelSync() + val folders = modelHelper.getBgDataModel().folders + + assertThat(folders.size()).isEqualTo(1) + assertThat(folders.valueAt(0).contents.size).isEqualTo(itemCount) + return folders.valueAt(0).contents + } + + private fun verifyHighRes(items: ArrayList<WorkspaceItemInfo>, vararg indices: Int) { + for (index in indices) { + assertWithMessage("Index $index was not highRes") + .that(items[index].bitmap.isNullOrLowRes) + .isFalse() + } + } + + private fun verifyLowRes(items: ArrayList<WorkspaceItemInfo>, vararg indices: Int) { + for (index in indices) { + assertWithMessage("Index $index was not lowRes") + .that(items[index].bitmap.isNullOrLowRes) + .isTrue() + } + } + + /** Recreate DeviceProfiles after changing InvariantDeviceProfile */ + private fun recreateSupportedDeviceProfiles() { + LauncherAppState.getIDP(modelHelper.sandboxContext).supportedProfiles = + LauncherAppState.getIDP(modelHelper.sandboxContext).supportedProfiles.map { + it.copy(modelHelper.sandboxContext) + } + } +} diff --git a/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java b/tests/src/com/android/launcher3/secondarydisplay/TaplSecondaryDisplayLauncherTest.java index c7431f20dd..d7b9638ef2 100644 --- a/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java +++ b/tests/src/com/android/launcher3/secondarydisplay/TaplSecondaryDisplayLauncherTest.java @@ -47,7 +47,7 @@ import org.junit.runner.RunWith; */ @LargeTest @RunWith(AndroidJUnit4.class) -public final class SecondaryDisplayLauncherTest extends AbstractLauncherUiTest { +public final class TaplSecondaryDisplayLauncherTest extends AbstractLauncherUiTest { private static final int WAIT_TIME_MS = 5000; private static final int LONG_PRESS_DURATION_MS = 1000; private static final int DRAG_TIME_MS = 160; diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 8ad224913c..79d8c60049 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -131,6 +131,8 @@ public abstract class AbstractLauncherUiTest { /** Detects activity leaks and throws an exception if a leak is found. */ public static void checkDetectedLeaks(LauncherInstrumentation launcher, boolean requireOneActiveActivityUnused) { + if (TestStabilityRule.isPresubmit()) return; // b/313501215 + final boolean requireOneActiveActivity = false; // workaround for leaks when there is an unexpected Recents activity @@ -255,7 +257,7 @@ public abstract class AbstractLauncherUiTest { final RuleChain inner = RuleChain .outerRule(new PortraitLandscapeRunner(this)) .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData)) - .around(viewCaptureRule) + // .around(viewCaptureRule) // b/315482167 .around(new TestIsolationRule(mLauncher, true)); return TestHelpers.isInLauncherProcess() diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java index 485ef9488d..f8185641bd 100644 --- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java +++ b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java @@ -48,7 +48,7 @@ import org.junit.Test; import java.util.Objects; import java.util.function.Predicate; -public class WorkProfileTest extends AbstractLauncherUiTest { +public class TaplWorkProfileTest extends AbstractLauncherUiTest { private static final int WORK_PAGE = ActivityAllAppsContainerView.AdapterHolder.WORK; diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java index b2ce400d8d..d96287f6f4 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java @@ -52,7 +52,7 @@ import org.junit.runner.RunWith; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class AddConfigWidgetTest extends AbstractLauncherUiTest { +public class TaplAddConfigWidgetTest extends AbstractLauncherUiTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); @@ -101,7 +101,7 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { // Verify that the widget id is valid and bound assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId)); - setResult(acceptConfig); + setResultAndWaitForAnimation(acceptConfig); if (acceptConfig) { Wait.atMost("", new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT, mLauncher); assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId)); @@ -112,12 +112,22 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { } } - private void setResult(boolean success) { + private static void setResult(boolean success) { getInstrumentation().getTargetContext().sendBroadcast( WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class, success ? "clickOK" : "clickCancel")); } + private void setResultAndWaitForAnimation(boolean success) { + if (mLauncher.isLauncher3()) { + setResult(success); + } else { + mLauncher.executeAndWaitForWallpaperAnimation( + () -> setResult(success), + "setting widget coinfig result"); + } + } + /** * Condition for searching widget id */ diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java index fd4b7f1f6c..27fda9b680 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java @@ -42,7 +42,7 @@ import org.junit.runner.RunWith; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class AddWidgetTest extends AbstractLauncherUiTest { +public class TaplAddWidgetTest extends AbstractLauncherUiTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java index 32793cc084..6aa746d6d3 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java @@ -76,7 +76,7 @@ import java.util.function.Consumer; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class BindWidgetTest extends AbstractLauncherUiTest { +public class TaplBindWidgetTest extends AbstractLauncherUiTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java index 3c88f1d69c..f12f96153b 100644 --- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java +++ b/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java @@ -59,7 +59,7 @@ import java.util.UUID; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class RequestPinItemTest extends AbstractLauncherUiTest { +public class TaplRequestPinItemTest extends AbstractLauncherUiTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); diff --git a/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java b/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java index 465e9b4153..bc736831b6 100644 --- a/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java +++ b/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java @@ -15,10 +15,6 @@ */ package com.android.launcher3.ui.widget; -import static com.android.launcher3.ui.AbstractLauncherUiTest.initialize; -import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; -import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -31,7 +27,6 @@ import com.android.launcher3.tapl.Widgets; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; -import com.android.launcher3.util.rule.TestStabilityRule.Stability; import com.android.launcher3.widget.picker.WidgetsFullSheet; import com.android.launcher3.widget.picker.WidgetsRecyclerView; @@ -65,11 +60,9 @@ public class TaplWidgetPickerTest extends AbstractLauncherUiTest { * Open Widget picker, make sure the widget picker can scroll and then go to home screen. */ @Test - @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/303263644 @ScreenRecord @PortraitLandscape public void testWidgets() { - // Testing if this will fix b/303263644 mLauncher.goHome(); // Test opening widgets. executeOnLauncher(launcher -> diff --git a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplThemeIconsTest.java index e21918f1e3..a3d33441d4 100644 --- a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java +++ b/tests/src/com/android/launcher3/ui/workspace/TaplThemeIconsTest.java @@ -49,7 +49,7 @@ import java.util.Queue; * Note running these tests will clear the workspace on the device. */ @LargeTest -public class ThemeIconsTest extends AbstractLauncherUiTest { +public class TaplThemeIconsTest extends AbstractLauncherUiTest { private static final String APP_NAME = "IconThemedActivity"; private static final String SHORTCUT_NAME = "Shortcut 1"; diff --git a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java index e7112d14b0..3693163d21 100644 --- a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java +++ b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java @@ -54,7 +54,7 @@ import java.util.stream.Collectors; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { +public class TaplTwoPanelWorkspaceTest extends AbstractLauncherUiTest { private AutoCloseable mLauncherLayout; diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java index 261436b3ac..244dc269b6 100644 --- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java +++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java @@ -84,6 +84,17 @@ public class LauncherModelHelper { 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"; + public static final String TEST_ACTIVITY4 = "com.android.launcher3.tests.Activity5"; + public static final String TEST_ACTIVITY5 = "com.android.launcher3.tests.Activity6"; + public static final String TEST_ACTIVITY6 = "com.android.launcher3.tests.Activity7"; + public static final String TEST_ACTIVITY7 = "com.android.launcher3.tests.Activity8"; + public static final String TEST_ACTIVITY8 = "com.android.launcher3.tests.Activity9"; + public static final String TEST_ACTIVITY9 = "com.android.launcher3.tests.Activity10"; + public static final String TEST_ACTIVITY10 = "com.android.launcher3.tests.Activity11"; + public static final String TEST_ACTIVITY11 = "com.android.launcher3.tests.Activity12"; + public static final String TEST_ACTIVITY12 = "com.android.launcher3.tests.Activity13"; + public static final String TEST_ACTIVITY13 = "com.android.launcher3.tests.Activity14"; + public static final String TEST_ACTIVITY14 = "com.android.launcher3.tests.Activity15"; // Authority for providing a test default-workspace-layout data. private static final String TEST_PROVIDER_AUTHORITY = diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java index 51b7b187ca..dfccffc1f5 100644 --- a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java @@ -33,17 +33,17 @@ final class AlphaJumpDetector extends AnomalyDetector { private static final IgnoreNode IGNORED_NODES_ROOT = buildIgnoreNodesTree(List.of( CONTENT - + "AddItemDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + + "SimpleDragLayer: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" + + "SimpleDragLayer: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", + CONTENT + "SimpleDragLayer:id/add_item_drag_layer|View", DRAG_LAYER + "AppWidgetResizeFrame|FrameLayout|ImageButton:id/widget_reconfigure_button", DRAG_LAYER diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java index e3330745e3..fc8f818bf5 100644 --- a/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java @@ -44,13 +44,13 @@ final class FlashDetector extends AnomalyDetector { + "LauncherAllAppsContainerView:id/apps_view|AllAppsRecyclerView:id" + "/apps_list_view|BubbleTextView:id/icon", CONTENT - + "AddItemDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + + "SimpleDragLayer: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" + + "SimpleDragLayer: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", diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/PositionJumpDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/PositionJumpDetector.java index 5f2c68cc41..88ace68d53 100644 --- a/tests/src/com/android/launcher3/util/viewcapture_analysis/PositionJumpDetector.java +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/PositionJumpDetector.java @@ -41,7 +41,7 @@ final class PositionJumpDetector extends AnomalyDetector { DRAG_LAYER + "AppWidgetResizeFrame", DRAG_LAYER + "LauncherAllAppsContainerView:id/apps_view", CONTENT - + "AddItemDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + + "SimpleDragLayer: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", diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java index ba024733d7..b27ccbfd2f 100644 --- a/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java @@ -35,7 +35,7 @@ public class ViewCaptureAnalyzer { // All detectors. They will be invoked in the order listed here. private static final AnomalyDetector[] ANOMALY_DETECTORS = { - new AlphaJumpDetector(), +// new AlphaJumpDetector(), // b/309014345 // new FlashDetector(), // b/309014345 new PositionJumpDetector() }; diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java index 7d25121f1b..0e785659a6 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java @@ -54,6 +54,7 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer private static final int MAX_SCROLL_ATTEMPTS = 40; private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background"; + private static final String FAST_SCROLLER_RES_ID = "fast_scroller"; private static final Pattern EVENT_ALT_ESC_DOWN = Pattern.compile( "Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_ESCAPE.*?metaState=0"); @@ -369,9 +370,12 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "want to tap outside AllApps bottom sheet on the " + (tapRight ? "right" : "left"))) { - final UiObject2 allAppsBottomSheet = + + final UiObject2 container = (tapRight) + ? mLauncher.waitForLauncherObject(FAST_SCROLLER_RES_ID) : mLauncher.waitForLauncherObject(BOTTOM_SHEET_RES_ID); - mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight); + + mLauncher.touchOutsideContainer(container, tapRight, false); try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer( "tapped outside AllApps bottom sheet")) { verifyVisibleContainerOnDismiss(); diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java index 44869bea50..b6b4a47a1a 100644 --- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java +++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java @@ -186,7 +186,14 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { Taskbar taskbar = new Taskbar(mLauncher); taskbar.touchBottomCorner(tapRight); - verifyActiveContainer(); + if (mLauncher.isTransientTaskbar()) { + // Tapping outside Transient Taskbar returns to Workspace, wait for that state. + new Workspace(mLauncher); + } else { + // Should stay in Overview. + verifyActiveContainer(); + verifyActionsViewVisibility(); + } } } diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java index d9b179c6e7..9ca2dc8463 100644 --- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java @@ -118,4 +118,17 @@ public class HomeAllApps extends AllApps { public boolean isHomeState() { return true; } + + /** Send the "back" gesture to go to workspace. */ + public Workspace pressBackToWorkspace() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "want to press back from all apps to workspace")) { + mLauncher.runToState( + () -> mLauncher.pressBackImpl(), + NORMAL_STATE_ORDINAL, + "pressing back"); + return new Workspace(mLauncher); + } + } } diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java index 2512175e6c..fe927b3fd0 100644 --- a/tests/tapl/com/android/launcher3/tapl/Launchable.java +++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java @@ -65,11 +65,9 @@ public abstract class Launchable { + mLauncher.getVisibleBounds(mObject)); if (launcherStopsAfterLaunch()) { - mLauncher.executeAndWaitForLauncherEvent( + mLauncher.executeAndWaitForLauncherStop( () -> mLauncher.clickLauncherObject(mObject), - event -> TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE - .equals(event.getClassName().toString()), - () -> "Launcher activity didn't stop", "clicking the launchable"); + "clicking the launchable"); } else { mLauncher.clickLauncherObject(mObject); } diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java index efeb5f6666..184ece74f5 100644 --- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java +++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java @@ -20,12 +20,11 @@ import static com.android.launcher3.tapl.LauncherInstrumentation.DEFAULT_POLL_IN import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID; import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS; import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_BLOCK_TIMEOUT; -import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING; import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_BLOCK_TIMEOUT; -import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING; import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_SHELL_DRAG_READY; -import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT; import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_TASKBAR_SCALE; +import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_TASKBAR_FROM_NAV_THRESHOLD; +import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD; import android.graphics.Point; import android.graphics.Rect; @@ -84,8 +83,6 @@ public final class LaunchedAppState extends Background { public Taskbar getTaskbar() { try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "want to get the taskbar")) { - mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID); - return new Taskbar(mLauncher); } } @@ -113,37 +110,32 @@ public final class LaunchedAppState extends Background { /** * Returns the Taskbar in a visible state. * - * The taskbar must already be hidden when calling this method. + * The taskbar must already be hidden and in transient mode when calling this method. */ - public Taskbar showTaskbar() { - mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING); + public Taskbar swipeUpToUnstashTaskbar() { + mLauncher.assertTrue("Taskbar is not transient, swipe up not supported", + mLauncher.isTransientTaskbar()); + mLauncher.getTestInfo(REQUEST_ENABLE_BLOCK_TIMEOUT); try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( - "want to show the taskbar")) { + "want to swipe up to unstash the taskbar")) { mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID); - final long downTime = SystemClock.uptimeMillis(); - final int unstashTargetY = mLauncher.getRealDisplaySize().y - - (mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_HEIGHT) - .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD) / 2); - final Point unstashTarget = new Point( - mLauncher.getRealDisplaySize().x / 2, unstashTargetY); + int taskbarFromNavThreshold = mLauncher.getTestInfo(REQUEST_TASKBAR_FROM_NAV_THRESHOLD) + .getInt(TEST_INFO_RESPONSE_FIELD); + int startX = mLauncher.getRealDisplaySize().x / 2; + int startY = mLauncher.getRealDisplaySize().y - 1; + int endX = startX; + int endY = startY - taskbarFromNavThreshold; - mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, unstashTarget, + mLauncher.linearGesture(startX, startY, endX, endY, 10, /* slowDown= */ true, LauncherInstrumentation.GestureScope.EXPECT_PILFER); - LauncherInstrumentation.log("showTaskbar: sent down"); + LauncherInstrumentation.log("swipeUpToUnstashTaskbar: sent linear swipe up gesture"); - try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) { - mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID); - mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, unstashTarget, - LauncherInstrumentation.GestureScope.EXPECT_PILFER); - - return new Taskbar(mLauncher); - } + return new Taskbar(mLauncher); } finally { - mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING); mLauncher.getTestInfo(REQUEST_DISABLE_BLOCK_TIMEOUT); } } @@ -348,4 +340,17 @@ public final class LaunchedAppState extends Background { } } } + + /** Send the "back" gesture to go to workspace. */ + public Workspace pressBackToWorkspace() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "want to press back from launched app to workspace")) { + mLauncher.executeAndWaitForWallpaperAnimation( + () -> mLauncher.pressBackImpl(), + "pressing back" + ); + return new Workspace(mLauncher); + } + } } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index dd8ab81a4b..91ef472ee4 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -585,6 +585,8 @@ public final class LauncherInstrumentation { if (hasSystemLauncherObject(OVERVIEW_RES_ID)) return "Overview"; if (hasLauncherObject(WORKSPACE_RES_ID)) return "Workspace"; if (hasLauncherObject(APPS_RES_ID)) return "AllApps"; + if (hasLauncherObject(TASKBAR_RES_ID)) return "Taskbar"; + if (hasLauncherObject("wallpaper_carousel")) return "Launcher Settings Popup"; if (mDevice.hasObject(By.pkg(getLauncherPackageName()).depth(0))) { return "<Launcher in invalid state>"; } @@ -823,12 +825,7 @@ public final class LauncherInstrumentation { waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID); waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID); waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID); - - if (is3PLauncher() && isTablet()) { - waitForSystemLauncherObject(TASKBAR_RES_ID); - } else { - waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID); - } + waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID); return waitForLauncherObject(WORKSPACE_RES_ID); } @@ -838,12 +835,7 @@ public final class LauncherInstrumentation { waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID); waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID); waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID); - - if (is3PLauncher() && isTablet()) { - waitForSystemLauncherObject(TASKBAR_RES_ID); - } else { - waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID); - } + waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID); return waitForLauncherObject(WIDGETS_RES_ID); } @@ -864,7 +856,7 @@ public final class LauncherInstrumentation { waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID); waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID); - if (is3PLauncher() && isTablet()) { + if (is3PLauncher() && isTablet() && !isTransientTaskbar()) { waitForSystemLauncherObject(TASKBAR_RES_ID); } else { waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID); @@ -877,7 +869,7 @@ public final class LauncherInstrumentation { waitUntilLauncherObjectGone(APPS_RES_ID); waitUntilLauncherObjectGone(WORKSPACE_RES_ID); waitUntilLauncherObjectGone(WIDGETS_RES_ID); - if (isTablet()) { + if (isTablet() && !is3PLauncher()) { waitForSystemLauncherObject(TASKBAR_RES_ID); } else { waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID); @@ -914,7 +906,11 @@ public final class LauncherInstrumentation { } if (isTablet()) { - waitForSystemLauncherObject(TASKBAR_RES_ID); + // Only check that Persistent Taskbar is visible, since Transient Taskbar + // may or may not be visible by design. + if (!isTransientTaskbar()) { + waitForSystemLauncherObject(TASKBAR_RES_ID); + } } else { waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID); } @@ -978,6 +974,14 @@ public final class LauncherInstrumentation { } } + void executeAndWaitForLauncherStop(Runnable command, String actionName) { + executeAndWaitForLauncherEvent( + () -> command.run(), + event -> TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE + .equals(event.getClassName().toString()), + () -> "Launcher activity didn't stop", actionName); + } + /** * Get the resource ID of visible floating view. */ @@ -994,7 +998,7 @@ public final class LauncherInstrumentation { /** * Using swiping up gesture to dismiss closable floating views, such as Menu or Folder Content. */ - private void swipeUpToCloseFloatingView(boolean gestureStartFromLauncher) { + private void swipeUpToCloseFloatingView() { final Point displaySize = getRealDisplaySize(); final Optional<String> floatingRes = getFloatingResId(); @@ -1003,16 +1007,11 @@ public final class LauncherInstrumentation { return; } - GestureScope gestureScope = gestureStartFromLauncher - // Without the navigation bar layer, the gesture scope on tablets remains inside the - // launcher process. - ? (isTablet() ? GestureScope.DONT_EXPECT_PILFER : GestureScope.EXPECT_PILFER) - : GestureScope.EXPECT_PILFER; linearGesture( displaySize.x / 2, displaySize.y - 1, displaySize.x / 2, 0, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, - false, gestureScope); + false, GestureScope.EXPECT_PILFER); try (LauncherInstrumentation.Closable c1 = addContextLayer( String.format("Swiped up from floating view %s to home", floatingRes.get()))) { @@ -1080,11 +1079,8 @@ public final class LauncherInstrumentation { final Point displaySize = getRealDisplaySize(); - boolean gestureStartFromLauncher = - isTablet() ? !isLauncher3() : isLauncherVisible(); - // CLose floating views before going back to home. - swipeUpToCloseFloatingView(gestureStartFromLauncher); + swipeUpToCloseFloatingView(); if (hasLauncherObject(WORKSPACE_RES_ID)) { log(action = "already at home"); @@ -1125,30 +1121,34 @@ public final class LauncherInstrumentation { */ public void pressBack() { try (Closable e = eventsCheck(); Closable c = addContextLayer("want to press back")) { - waitForLauncherInitialized(); - final boolean launcherVisible = - isTablet() ? isLauncherContainerVisible() : isLauncherVisible(); - boolean isThreeFingerTrackpadGesture = - mTrackpadGestureType == TrackpadGestureType.THREE_FINGER; - if (getNavigationModel() == NavigationModel.ZERO_BUTTON - || isThreeFingerTrackpadGesture) { - final Point displaySize = getRealDisplaySize(); - // TODO(b/225505986): change startY and endY back to displaySize.y / 2 once the - // issue is solved. - 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); + pressBackImpl(); + } + } + + void pressBackImpl() { + waitForLauncherInitialized(); + final boolean launcherVisible = + isTablet() ? isLauncherContainerVisible() : isLauncherVisible(); + boolean isThreeFingerTrackpadGesture = + mTrackpadGestureType == TrackpadGestureType.THREE_FINGER; + if (getNavigationModel() == NavigationModel.ZERO_BUTTON + || isThreeFingerTrackpadGesture) { + final Point displaySize = getRealDisplaySize(); + // TODO(b/225505986): change startY and endY back to displaySize.y / 2 once the + // issue is solved. + 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 (launcherVisible) { + if (getContext().getApplicationInfo().isOnBackInvokedCallbackEnabled()) { + expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ON_BACK_INVOKED); } else { - waitForNavigationUiObject("back").click(); - } - if (launcherVisible) { - if (getContext().getApplicationInfo().isOnBackInvokedCallbackEnabled()) { - expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ON_BACK_INVOKED); - } else { - expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN); - expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP); - } + expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN); + expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP); } } } @@ -2038,6 +2038,7 @@ public final class LauncherInstrumentation { } } + /** Returns the bounds of the display as a Point where x is width and y is height. */ Point getRealDisplaySize() { final Rect displayBounds = getContext().getSystemService(WindowManager.class) .getMaximumWindowMetrics() @@ -2100,6 +2101,11 @@ public final class LauncherInstrumentation { : TestProtocol.REQUEST_DISABLE_BLOCK_TIMEOUT); } + public boolean isTransientTaskbar() { + return getTestInfo(TestProtocol.REQUEST_IS_TRANSIENT_TASKBAR) + .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD); + } + /** Enables transient taskbar for testing purposes only. */ public void enableTransientTaskbar(boolean enable) { getTestInfo(enable @@ -2187,7 +2193,8 @@ public final class LauncherInstrumentation { }; } - boolean isLauncher3() { + /** Returns whether the Launcher is a Launcher3 one */ + public boolean isLauncher3() { if (mIsLauncher3 == null) { mIsLauncher3 = "com.android.launcher3".equals(getLauncherPackageName()); } @@ -2312,4 +2319,14 @@ public final class LauncherInstrumentation { } return result; } + + /** Executes a runnable and waits for the wallpaper-open animation completion. */ + public void executeAndWaitForWallpaperAnimation(Runnable r, String actionName) { + executeAndWaitForLauncherEvent( + () -> r.run(), + event -> TestProtocol.WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE + .equals(event.getClassName().toString()), + () -> "Didn't detect finishing wallpaper-open animation", + actionName); + } } diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java index 8a34f0da94..f383e99584 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java @@ -16,8 +16,6 @@ package com.android.launcher3.tapl; -import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; - import android.graphics.Rect; import androidx.annotation.NonNull; @@ -192,8 +190,8 @@ public final class OverviewTask { private List<Integer> getCurrentTasksCenterXList() { return mLauncher.isTablet() ? mOverview.getCurrentTasksForTablet().stream() - .map(OverviewTask::getTaskCenterX) - .collect(Collectors.toList()) + .map(OverviewTask::getTaskCenterX) + .collect(Collectors.toList()) : List.of(mOverview.getCurrentTask().getTaskCenterX()); } @@ -203,11 +201,8 @@ public final class OverviewTask { public LaunchedAppState open() { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { verifyActiveContainer(); - mLauncher.executeAndWaitForEvent( + mLauncher.executeAndWaitForLauncherStop( () -> mLauncher.clickLauncherObject(mTask), - event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED, - () -> "Launching task didn't open a new window: " - + mTask.getParent().getContentDescription(), "clicking an overview task"); if (mOverview.getContainerType() == LauncherInstrumentation.ContainerType.SPLIT_SCREEN_SELECT) { diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java index 38cc321e59..3d2914d754 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java @@ -59,8 +59,10 @@ public class OverviewTaskMenu { 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"))); + mLauncher.executeAndWaitForLauncherStop( + () -> mLauncher.clickLauncherObject( + mLauncher.findObjectInContainer(mMenu, By.text("App info"))), + "tapped app info menu item"); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( "tapped app info menu item")) { diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java index da26694aae..a202c53375 100644 --- a/tests/tapl/com/android/launcher3/tapl/Taskbar.java +++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java @@ -18,8 +18,6 @@ package com.android.launcher3.tapl; import static android.view.KeyEvent.KEYCODE_META_RIGHT; import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID; -import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING; -import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING; import android.graphics.Point; import android.graphics.Rect; @@ -33,6 +31,8 @@ import androidx.test.uiautomator.By; import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiObject2; +import org.junit.Assert; + import java.util.List; import java.util.stream.Collectors; @@ -45,6 +45,15 @@ public final class Taskbar { Taskbar(LauncherInstrumentation launcher) { mLauncher = launcher; + try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "expect new taskbar to be visible")) { + mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID); + } + + if (!mLauncher.isTransientTaskbar()) { + Assert.assertEquals("Persistent taskbar should fill screen width", + getVisibleBounds().width(), mLauncher.getRealDisplaySize().x); + } } /** @@ -61,33 +70,32 @@ public final class Taskbar { } /** - * Hides this taskbar. - * - * The taskbar must already be visible when calling this method. + * Stashes this taskbar. + * <p> + * The taskbar must already be unstashed and in transient mode when calling this method. */ - public void hide() { - mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING); + public void swipeDownToStash() { + mLauncher.assertTrue("Taskbar is not transient, swipe down not supported", + mLauncher.isTransientTaskbar()); try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "want to hide the taskbar"); LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID); - final long downTime = SystemClock.uptimeMillis(); - Point stashTarget = new Point( - mLauncher.getRealDisplaySize().x - 1, mLauncher.getRealDisplaySize().y - 1); + Rect taskbarBounds = getVisibleBounds(); + int startX = taskbarBounds.centerX(); + int startY = taskbarBounds.centerY(); + int endX = startX; + int endY = mLauncher.getRealDisplaySize().y - 1; - mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, stashTarget, + mLauncher.linearGesture(startX, startY, endX, endY, 10, false, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); - LauncherInstrumentation.log("hideTaskbar: sent down"); - - try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) { + LauncherInstrumentation.log("swipeDownToStash: sent linear swipe down gesture"); + try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( + "expect transient taskbar to be hidden after swipe down")) { mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID); - mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, stashTarget, - LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); } - } finally { - mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING); } } diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 2a2a83f33f..f8fa00c365 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -335,7 +335,8 @@ public final class Workspace extends Home { homeAppIcon, () -> new Point(0, 0), () -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), - null); + null, + /* startsActivity = */ false); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( "dragged the app across workspace")) { @@ -359,7 +360,8 @@ public final class Workspace extends Home { homeAppIcon, () -> getDropPointFromDropTargetBar(mLauncher, DELETE_TARGET_TEXT_ID), () -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), - /* expectDropEvents= */ null); + /* expectDropEvents= */ null, + /* startsActivity = */ false); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( "dragged the app to the drop bar")) { @@ -368,7 +370,6 @@ public final class Workspace extends Home { } } - /** * Uninstall the appIcon by dragging it to the 'uninstall' drop point of the drop_target_bar. * @@ -390,7 +391,8 @@ public final class Workspace extends Home { homeAppIcon, () -> getDropPointFromDropTargetBar(launcher, UNINSTALL_TARGET_TEXT_ID), expectLongClickEvents, - /* expectDropEvents= */null); + /* expectDropEvents= */null, + /* startsActivity = */ false); launcher.waitUntilLauncherObjectGone(DROP_BAR_RES_ID); @@ -464,14 +466,25 @@ public final class Workspace extends Home { o -> new FolderIcon(mLauncher, o)).collect(Collectors.toList()); } + private static void sendUp(LauncherInstrumentation launcher, Point dest, + long downTime) { + launcher.sendPointer( + downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest, + LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); + } + private static void dropDraggedIcon(LauncherInstrumentation launcher, Point dest, long downTime, - @Nullable Runnable expectedEvents) { - launcher.runToState( - () -> launcher.sendPointer( - downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest, - LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER), - NORMAL_STATE_ORDINAL, - "sending UP event"); + @Nullable Runnable expectedEvents, boolean startsActivity) { + if (startsActivity) { + launcher.executeAndWaitForLauncherStop( + () -> sendUp(launcher, dest, downTime), + "sending UP event"); + } else { + launcher.runToState( + () -> sendUp(launcher, dest, downTime), + NORMAL_STATE_ORDINAL, + "sending UP event"); + } if (expectedEvents != null) { expectedEvents.run(); } @@ -488,7 +501,8 @@ public final class Workspace extends Home { LauncherInstrumentation.EVENT_START); } dragIconToWorkspace( - launcher, launchable, dest, expectLongClickEvents, expectDropEvents); + launcher, launchable, dest, expectLongClickEvents, expectDropEvents, + startsActivity); } static void dragIconToWorkspaceCellPosition(LauncherInstrumentation launcher, @@ -517,7 +531,8 @@ public final class Workspace extends Home { destSupplier, /* isDecelerating= */ false, () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), - /* expectDropEvents= */ null); + /* expectDropEvents= */ null, + /* startsActivity = */ false); } static void dragIconToWorkspace( @@ -525,9 +540,10 @@ public final class Workspace extends Home { Launchable launchable, Supplier<Point> dest, Runnable expectLongClickEvents, - @Nullable Runnable expectDropEvents) { + @Nullable Runnable expectDropEvents, + boolean startsActivity) { dragIconToWorkspace(launcher, launchable, dest, /* isDecelerating */ true, - expectLongClickEvents, expectDropEvents); + expectLongClickEvents, expectDropEvents, startsActivity); } static void dragIconToWorkspace( @@ -536,7 +552,8 @@ public final class Workspace extends Home { Supplier<Point> dest, boolean isDecelerating, Runnable expectLongClickEvents, - @Nullable Runnable expectDropEvents) { + @Nullable Runnable expectDropEvents, + boolean startsActivity) { try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer( "want to drag icon to workspace")) { final long downTime = SystemClock.uptimeMillis(); @@ -568,7 +585,7 @@ public final class Workspace extends Home { launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating, downTime, SystemClock.uptimeMillis(), false, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); - dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents); + dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents, startsActivity); } } @@ -601,7 +618,8 @@ public final class Workspace extends Home { launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating, downTime, SystemClock.uptimeMillis(), false, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); - dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents); + dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents, + /* startsActivity = */ false); } } @@ -667,7 +685,8 @@ public final class Workspace extends Home { launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, true, downTime, SystemClock.uptimeMillis(), false, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); - dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents); + dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents, + /* startsActivity = */ false); } /** diff --git a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java index 5a4d562474..e5a2a2ef6c 100644 --- a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java +++ b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java @@ -81,7 +81,8 @@ interface WorkspaceDragSource { launchable, dest, launchable::addExpectedEventsForLongClick, - /*expectDropEvents= */ null); + /*expectDropEvents= */ null, + /* startsActivity = */ false); try (LauncherInstrumentation.Closable ignore = launcher.addContextLayer("dragged")) { WorkspaceAppIcon appIcon = |