Merge "End icon alignment early when touching down during 3 button anim to home" into main
diff --git a/Android.bp b/Android.bp
index 223e2c2..73d0fce 100644
--- a/Android.bp
+++ b/Android.bp
@@ -387,6 +387,7 @@
"//frameworks/libs/systemui:view_capture",
"//frameworks/libs/systemui:animationlib",
"//frameworks/libs/systemui:contextualeducationlib",
+ "//frameworks/libs/systemui:msdl",
"SystemUI-statsd",
"launcher-testing-shared",
"androidx.lifecycle_lifecycle-common-java8",
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index ee7e975..1856b39 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -519,4 +519,18 @@
namespace: "launcher"
description: "Enable MSDL feedback for Launcher interactions"
bug: "377496684"
+}
+
+flag {
+ name: "taskbar_recents_layout_transition"
+ namespace: "launcher"
+ description: "Enable Taskbar LayoutTransition for Recent Apps"
+ bug: "343521765"
+}
+
+flag {
+ name: "enable_pinning_app_with_context_menu"
+ namespace: "launcher"
+ description: "Add options to pin/unpin to taskbar to app context menus."
+ bug: "375648361"
}
\ No newline at end of file
diff --git a/aconfig/launcher_overview.aconfig b/aconfig/launcher_overview.aconfig
index 4335f76..93d8d54 100644
--- a/aconfig/launcher_overview.aconfig
+++ b/aconfig/launcher_overview.aconfig
@@ -54,4 +54,11 @@
namespace: "launcher_overview"
description: "Makes the desktop windowing task carousel detaches from fullscreen task carousel during quickswitch."
bug: "353947917"
+}
+
+flag {
+ name: "enable_desktop_exploded_view"
+ namespace: "launcher_overview"
+ description: "Enables the non-overlapping layout for desktop windows in Overview mode."
+ bug: "378011776"
}
\ No newline at end of file
diff --git a/go/quickstep/res/values-ne/strings.xml b/go/quickstep/res/values-ne/strings.xml
index e66f063..4f771c3 100644
--- a/go/quickstep/res/values-ne/strings.xml
+++ b/go/quickstep/res/values-ne/strings.xml
@@ -9,11 +9,11 @@
<string name="dialog_cancel" msgid="6464336969134856366">"रद्द गर्नुहोस्"</string>
<string name="dialog_settings" msgid="6564397136021186148">"सेटिङ"</string>
<string name="niu_actions_confirmation_title" msgid="3863451714863526143">"स्क्रिनमा देखिने पाठ अनुवाद गर्नुहोस् वा पढेर सुनाउनुहोस्"</string>
- <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"तपाईंको स्क्रिनमा देखिने पाठ, वेब ठेगाना र स्क्रिनसटलगायतका जानकारी Google सँग सेयर गर्न सकिन्छ।\n\nकुन कुन जानकारी सेयर गर्न दिने भन्ने सेटिङ बदल्न "<b>"सेटिङ > एप > डिफल्ट एप > डिजिटल सहायक एप"</b>" मा जानुहोस्।"</string>
+ <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"तपाईंको स्क्रिनमा देखिने पाठ, वेब ठेगाना र स्क्रिनसटलगायतका जानकारी Google सँग सेयर गर्न सकिन्छ।\n\nकुन कुन जानकारी सेयर गर्न दिने भन्ने सेटिङ बदल्न "<b>"सेटिङ > एप > डिफल्ट एप > डिजिटल एसिस्टेन्ट एप"</b>" मा जानुहोस्।"</string>
<string name="assistant_not_selected_title" msgid="5017072974603345228">"तपाईं यो सुविधा चलाउन चाहनुहुन्छ भने कुनै सहायक छनौट गर्नुहोस्"</string>
- <string name="assistant_not_selected_text" msgid="3244613673884359276">"तपाईं आफ्नो स्क्रिनमा देखिने पाठ सुन्न वा अनुवाद गर्न चाहनुहुन्छ भने सेटिङमा गई कुनै डिजिटल सहायक एप छनौट गर्नुहोस्"</string>
+ <string name="assistant_not_selected_text" msgid="3244613673884359276">"तपाईं आफ्नो स्क्रिनमा देखिने पाठ सुन्न वा अनुवाद गर्न चाहनुहुन्छ भने सेटिङमा गई कुनै डिजिटल एसिस्टेन्ट एप छनौट गर्नुहोस्"</string>
<string name="assistant_not_supported_title" msgid="1675788067597484142">"तपाईं यो सुविधा चलाउन चाहनुहुन्छ भने आफ्नो सहायक परिवर्तन गर्नुहोस्"</string>
- <string name="assistant_not_supported_text" msgid="1708031078549268884">"तपाईं आफ्नो स्क्रिनमा देखिने पाठ सुन्न वा अनुवाद गर्न चाहनुहुन्छ भने सेटिङमा गई कुनै डिजिटल सहायक एप परिर्वर्तन गर्नुहोस्"</string>
+ <string name="assistant_not_supported_text" msgid="1708031078549268884">"तपाईं आफ्नो स्क्रिनमा देखिने पाठ सुन्न वा अनुवाद गर्न चाहनुहुन्छ भने सेटिङमा गई कुनै डिजिटल एसिस्टेन्ट एप परिर्वर्तन गर्नुहोस्"</string>
<string name="tooltip_listen" msgid="7634466447860989102">"तपाईं यो स्क्रिनमा देखिने पाठ सुन्न चाहनुहुन्छ यहाँ ट्याप गर्नुहोस्"</string>
<string name="tooltip_translate" msgid="4184845868901542567">"तपाईं यो स्क्रिनमा देखिने पाठ अनुवाद गर्न चाहनुहुन्छ यहाँ ट्याप गर्नुहोस्"</string>
<string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"यो एप अरूलाई चलाउन दिन मिल्दैन"</string>
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 57bfb4a..8c39585 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -152,7 +152,7 @@
android:showOnLockScreen="true"
android:launchMode="singleTop"
android:exported="true"
- android:permission="android.permission.START_WIDGET_PICKER_ACTIVITY">
+ android:permission="${applicationId}.permission.START_WIDGET_PICKER_ACTIVITY">
<intent-filter>
<action android:name="android.intent.action.PICK" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/quickstep/res/drawable/desktop_mode_ic_taskbar_menu_manage_windows.xml b/quickstep/res/drawable/desktop_mode_ic_taskbar_menu_manage_windows.xml
new file mode 100644
index 0000000..7d912a2
--- /dev/null
+++ b/quickstep/res/drawable/desktop_mode_ic_taskbar_menu_manage_windows.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="960" android:viewportHeight="960" android:tint="?attr/colorControlNormal">
+ <path android:fillColor="@android:color/black" android:pathData="M160,880Q127,880 103.5,856.5Q80,833 80,800L80,440Q80,407 103.5,383.5Q127,360 160,360L240,360L240,160Q240,127 263.5,103.5Q287,80 320,80L800,80Q833,80 856.5,103.5Q880,127 880,160L880,520Q880,553 856.5,576.5Q833,600 800,600L720,600L720,800Q720,833 696.5,856.5Q673,880 640,880L160,880ZM160,800L640,800Q640,800 640,800Q640,800 640,800L640,520L160,520L160,800Q160,800 160,800Q160,800 160,800ZM720,520L800,520Q800,520 800,520Q800,520 800,520L800,240L320,240L320,360L640,360Q673,360 696.5,383.5Q720,407 720,440L720,520Z"/>
+</vector>
diff --git a/quickstep/res/layout/keyboard_quick_switch_view.xml b/quickstep/res/layout/keyboard_quick_switch_view.xml
index 2420a46..4118500 100644
--- a/quickstep/res/layout/keyboard_quick_switch_view.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_view.xml
@@ -22,6 +22,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/keyboard_quick_switch_margin_top"
android:layout_marginHorizontal="@dimen/keyboard_quick_switch_margin_ends"
+ android:layout_gravity="center_horizontal"
android:background="@drawable/keyboard_quick_switch_view_background"
android:clipToOutline="true"
android:alpha="0"
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index f868baa..de550d1 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -52,8 +52,8 @@
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Přejeďte prstem z úplného pravého nebo levého okraje obrazovky"</string>
<string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Přejeďte prstem z pravého nebo levého okraje doprostřed obrazovky a zdvihněte prst"</string>
<string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Naučili jste se, jak se vrátit zpět přejetím prstem zprava. Teď se naučíte přepínat mezi aplikacemi."</string>
- <string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"Dokončili jste gesto pro přechod zpět. Teď se naučíte přepínat aplikace."</string>
- <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Dokončili jste gesto pro přechod zpět"</string>
+ <string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"Provedli jste gesto pro přechod zpět. Teď se naučíte přepínat aplikace."</string>
+ <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Provedli jste gesto pro přechod zpět"</string>
<string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Dejte pozor, abyste prstem nepřejížděli moc blízko ke spodnímu okraji obrazovky"</string>
<string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Citlivost gesta pro přechod zpět můžete změnit v Nastavení"</string>
<string name="back_gesture_intro_title" msgid="19551256430224428">"Přejetím prstem se vrátíte zpět"</string>
@@ -64,8 +64,8 @@
<string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Přejeďte prstem nahoru z dolního okraje obrazovky"</string>
<string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Před zdvihnutím prstu nedělejte pauzu"</string>
<string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Přejeďte prstem přímo nahoru"</string>
- <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Dokončili jste gesto pro přechod na plochu. Teď se naučíte vrátit se zpět."</string>
- <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Dokončili jste gesto pro přechod na plochu"</string>
+ <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Provedli jste gesto pro přechod na plochu. Teď se naučíte vrátit se zpět."</string>
+ <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Provedli jste gesto pro přechod na plochu"</string>
<string name="home_gesture_intro_title" msgid="836590312858441830">"Přechod na plochu přejetím prstem"</string>
<string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Přejeďte prstem ze spodní části obrazovky nahoru. Tímto gestem se vždy dostanete na plochu."</string>
<string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Přejeďte dvěma prsty z dolního okraje obrazovky nahoru. Tímto gestem se vždy dostanete na plochu."</string>
@@ -76,7 +76,7 @@
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Zkuste podržet okno delší dobu, než ho uvolníte"</string>
<string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Přejeďte prstem přímo nahoru a pak udělejte pauzu"</string>
<string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Naučili jste se používat gesta. Vypnout je můžete v Nastavení."</string>
- <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Dokončili jste gesto pro přepínání aplikací"</string>
+ <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Provedli jste gesto pro přepínání aplikací"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"Přepínání aplikací přejetím prstem"</string>
<string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Přejeďte nahoru z dolního okraje obrazovky, podržte obrazovku a uvolněte."</string>
<string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Přepínání mezi aplikacemi: Přejeďte dvěma prsty nahoru z dolního okraje obrazovky, podržte obrazovku a uvolněte."</string>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 07ab18b..b022172 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -52,8 +52,8 @@
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Stryg fra kanten yderst til højre eller venstre"</string>
<string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Stryg fra højre eller venstre kant mod midten af skærmen, og løft fingeren"</string>
<string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Du har lært, hvordan du stryger fra højre for at gå tilbage. Nu skal du se, hvordan du skifter app."</string>
- <string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"Du har fuldført bevægelsen for Gå tilbage. Som det næste kan du se, hvordan du skifter app."</string>
- <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Du har fuldført bevægelsen for Gå tilbage"</string>
+ <string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"Du har udført bevægelsen for Gå tilbage. Som det næste kan du se, hvordan du skifter app."</string>
+ <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Du har udført bevægelsen for Gå tilbage"</string>
<string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Undgå at stryge for tæt på bunden af skærmen"</string>
<string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Juster følsomheden for bevægelsen Gå tilbage i Indstillinger"</string>
<string name="back_gesture_intro_title" msgid="19551256430224428">"Stryg for at gå tilbage"</string>
@@ -64,8 +64,8 @@
<string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Stryg opad fra bunden af skærmen"</string>
<string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Undlad at holde fingeren stille, indtil du løfter fingeren"</string>
<string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Stryg lige opad"</string>
- <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Du har fuldført bevægelsen for Gå til startskærmen. Som det næste kan du se, hvordan du går tilbage."</string>
- <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Du har fuldført bevægelsen for Gå til startskærmen"</string>
+ <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Du har udført bevægelsen for Gå til startskærmen. Som det næste kan du se, hvordan du går tilbage."</string>
+ <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"Du har udført bevægelsen for Gå til startskærmen"</string>
<string name="home_gesture_intro_title" msgid="836590312858441830">"Stryg for at gå til startskærmen"</string>
<string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Stryg opad fra bunden af skærmen. Denne bevægelse åbner altid startskærmen."</string>
<string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Stryg opad med 2 fingre fra bunden af skærmen. Denne bevægelse åbner altid startskærmen."</string>
@@ -76,7 +76,7 @@
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Prøv at holde fingeren nede på vinduet i længere tid, inden du løfter den"</string>
<string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Stryg lige opad, og hold derefter fingeren stille"</string>
<string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Du har lært, hvordan du bruger bevægelser. Du kan aktivere bevægelser i Indstillinger."</string>
- <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Du har fuldført bevægelsen for at skifte mellem apps"</string>
+ <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Du har udført bevægelsen for at skifte mellem apps"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"Stryg for at skifte app"</string>
<string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Skift mellem apps ved at stryge opad fra bunden af skærmen, holde fingeren stille og løfte den."</string>
<string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Skift mellem apps ved at stryge opad fra bunden af skærmen med 2 fingre, holde dem nede og slippe."</string>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index 49d1664..0f374ba 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -52,8 +52,8 @@
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"स्क्रिनको सबैभन्दा दायाँ किनारा वा सबैभन्दा बायाँ किनाराबाट स्वाइप गर्नुहोस्"</string>
<string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"स्क्रिनको दायाँ वा बायाँ किनाराबाट मध्य भागसम्म स्वाइप गर्नुहोस् अनि औँला उठाउनुहोस्"</string>
<string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"तपाईंले स्क्रिनको दायाँ किनाराबाट स्वाइप गरेर अघिल्लो स्क्रिनमा फर्कने तरिका सिक्नुभयो। अब एउटा एपबाट अर्को एपमा जाने तरिका सिक्नुहोस्।"</string>
- <string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"तपाईंले \'पछाडि जानुहोस्\' नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो। अब एउटा एपबाट अर्को एपमा जाने तरिका सिक्नुहोस्।"</string>
- <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"तपाईंले \"पछाडि जानुहोस्\" नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो"</string>
+ <string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"तपाईंले जेस्चर प्रयोग गरी पछाडि जाने तरिका सिक्नुभएको छ। अब एउटा एपबाट अर्को एपमा जाने तरिका सिक्नुहोस्।"</string>
+ <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"तपाईंले जेस्चर प्रयोग गरी पछाडि जाने तरिका सिक्नुभएको छ"</string>
<string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"स्क्रिनको फेदको धेरै नजिकसम्म स्वाइप नगर्नुहोस्"</string>
<string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"\'पछाडि\' नामक इसाराको संवेदनशीलता बदल्न सेटिङमा जानुहोस्"</string>
<string name="back_gesture_intro_title" msgid="19551256430224428">"पछाडि जान स्वाइप गर्नुहोस्"</string>
@@ -64,7 +64,7 @@
<string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्"</string>
<string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"औँला उठाउनुअघि नरोकिनुहोस्"</string>
<string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"सीधै माथितिर स्वाइप गर्नुहोस्"</string>
- <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"तपाईंले \"होम स्क्रिनमा जानुहोस्\" नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो। अब पछाडि जाने तरिका सिक्नुहोस्।"</string>
+ <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"तपाईंले जेस्चर प्रयोग गरी होम स्क्रिनमा जाने तरिका सिक्नुभएको छ। अब पछाडि जाने तरिका सिक्नुहोस्।"</string>
<string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"तपाईंले \"होम स्क्रिनमा जानुहोस्\" नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो"</string>
<string name="home_gesture_intro_title" msgid="836590312858441830">"होम स्क्रिनमा जान स्वाइप गर्नुहोस्"</string>
<string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्। यो इसारा प्रयोग गर्दा सधैँ होम स्क्रिन खुल्छ।"</string>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 99b53d9..30e73e6 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -52,8 +52,8 @@
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ปัดจากขอบด้านขวาสุดหรือซ้ายสุด"</string>
<string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"ตรวจสอบว่าปัดจากขอบด้านขวาหรือซ้ายไปตรงกลางหน้าจอ แล้วยกนิ้วขึ้น"</string>
<string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"คุณรู้วิธีปัดจากด้านขวาเพื่อย้อนกลับแล้ว ต่อไปดูวิธีสลับแอป"</string>
- <string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับเสร็จแล้ว ต่อไปดูวิธีสลับแอป"</string>
- <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับเสร็จแล้ว"</string>
+ <string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับสำเร็จแล้ว ต่อไปดูวิธีสลับแอป"</string>
+ <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับสำเร็จแล้ว"</string>
<string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"ไม่ปัดใกล้กับด้านล่างของหน้าจอมากเกินไป"</string>
<string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"เปลี่ยนความไวของท่าทางสัมผัสเพื่อย้อนกลับได้ที่การตั้งค่า"</string>
<string name="back_gesture_intro_title" msgid="19551256430224428">"ปัดเพื่อย้อนกลับ"</string>
@@ -64,8 +64,8 @@
<string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"ปัดขึ้นจากขอบด้านล่างของหน้าจอ"</string>
<string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"ไม่ต้องหยุดชั่วคราวก่อนยกนิ้วขึ้น"</string>
<string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"ปัดขึ้นในแนวตรง"</string>
- <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกเสร็จแล้ว ต่อไปดูวิธีย้อนกลับ"</string>
- <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกเสร็จแล้ว"</string>
+ <string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกสำเร็จแล้ว ต่อไปดูวิธีย้อนกลับ"</string>
+ <string name="home_gesture_feedback_complete_without_follow_up" msgid="2978063221383413443">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกสำเร็จแล้ว"</string>
<string name="home_gesture_intro_title" msgid="836590312858441830">"ปัดเพื่อไปที่หน้าแรก"</string>
<string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ปัดขึ้นจากด้านล่างของหน้าจอ ท่าทางสัมผัสนี้จะนำคุณไปที่หน้าจอหลักเสมอ"</string>
<string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"ใช้ 2 นิ้วปัดขึ้นจากด้านล่างของหน้าจอ ท่าทางสัมผัสนี้จะนำคุณไปที่หน้าจอหลักเสมอ"</string>
@@ -76,7 +76,7 @@
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"ลองแตะหน้าต่างค้างไว้นานขึ้นก่อนปล่อยนิ้ว"</string>
<string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"ปัดขึ้นในแนวตรง แล้วหยุดชั่วคราว"</string>
<string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"คุณรู้วิธีใช้ท่าทางสัมผัสแล้ว หากต้องการปิดท่าทางสัมผัส ให้ไปที่การตั้งค่า"</string>
- <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"คุณทำท่าทางสัมผัสเพื่อสลับแอปเสร็จแล้ว"</string>
+ <string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"คุณทำท่าทางสัมผัสเพื่อสลับแอปสำเร็จแล้ว"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"ปัดเพื่อสลับแอป"</string>
<string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"หากต้องการสลับระหว่างแอปต่างๆ ให้ปัดขึ้นจากด้านล่างของหน้าจอ ค้างไว้ แล้วปล่อย"</string>
<string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"หากต้องการสลับระหว่างแอป ให้ใช้ 2 นิ้วปัดขึ้นจากด้านล่างของหน้าจอค้างไว้แล้วปล่อย"</string>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 3a32551..7eeacde 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -53,7 +53,7 @@
<string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Hãy vuốt từ mép phải hoặc mép trái tới giữa màn hình rồi nhấc ngón tay ra"</string>
<string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Bạn đã học được cách vuốt từ mép phải để quay lại. Tiếp theo, hãy tìm hiểu cách chuyển đổi ứng dụng."</string>
<string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"Bạn đã thực hiện xong cử chỉ quay lại. Tiếp theo, hãy tìm hiểu cách chuyển đổi ứng dụng."</string>
- <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Bạn đã thực hiện xong cử chỉ quay lại"</string>
+ <string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Bạn đã hoàn tất cử chỉ quay lại"</string>
<string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Hãy nhớ không được vuốt quá gần phần dưới cùng của màn hình"</string>
<string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Để thay đổi độ nhạy của cử chỉ quay lại, hãy vào mục Cài đặt"</string>
<string name="back_gesture_intro_title" msgid="19551256430224428">"Vuốt để quay lại"</string>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 782a705..53f37ba 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -118,6 +118,7 @@
<!-- Launcher app transition -->
<dimen name="closing_window_trans_y">115dp</dimen>
+ <dimen name="closing_freeform_window_trans_y">36dp</dimen>
<dimen name="quick_switch_scaling_scroll_threshold">100dp</dimen>
@@ -361,7 +362,11 @@
<dimen name="taskbar_running_app_indicator_width">12dp</dimen>
<dimen name="taskbar_running_app_indicator_top_margin">4dp</dimen>
<dimen name="taskbar_minimized_app_indicator_width">6dp</dimen>
- <dimen name="taskbar_overflow_button_preview_stroke">2dp</dimen>
+ <dimen name="taskbar_overflow_item_icon_size_default">22dp</dimen>
+ <dimen name="taskbar_overflow_item_icon_size_scaled_down">15dp</dimen>
+ <dimen name="taskbar_overflow_item_icon_stroke_width_default">2dp</dimen>
+ <dimen name="taskbar_overflow_leave_behind_size_default">18dp</dimen>
+ <dimen name="taskbar_overflow_leave_behind_size_scaled_down">15dp</dimen>
<!-- Transient taskbar -->
<dimen name="transient_taskbar_padding">12dp</dimen>
@@ -426,6 +431,9 @@
<dimen name="taskbar_pinning_popup_menu_vertical_margin">16dp</dimen>
<dimen name="taskbar_pinning_popup_menu_min_padding_from_screen_edge">16dp</dimen>
+ <!-- Taskbar Multi Instance Menu -->
+ <dimen name="taskbar_multi_instance_menu_min_padding_from_screen_edge">8dp</dimen>
+
<!--- Floating Ime Inset height-->
<dimen name="floating_ime_inset_height">60dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 18337d3..e624be7 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -108,6 +108,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import android.window.DesktopModeFlags;
import android.window.RemoteTransition;
import android.window.TransitionFilter;
import android.window.WindowAnimationState;
@@ -166,11 +167,13 @@
import com.android.systemui.shared.system.BlurUtils;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.startingsurface.IStartingWindowListener;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
@@ -214,6 +217,7 @@
public static final int CONTENT_ALPHA_DURATION = 217;
public static final int TRANSIENT_TASKBAR_TRANSITION_DURATION = 417;
+ public static final int PINNED_TASKBAR_TRANSITION_DURATION = 600;
public static final int TASKBAR_TO_APP_DURATION = 600;
// TODO(b/236145847): Tune TASKBAR_TO_HOME_DURATION to 383 after conflict with unlock animation
// is solved.
@@ -233,6 +237,7 @@
protected final Handler mHandler;
private final float mClosingWindowTransY;
+ private final float mClosingFreeformWindowTransY;
private final float mMaxShadowRadius;
private final StartingWindowListener mStartingWindowListener =
@@ -290,6 +295,8 @@
Resources res = mLauncher.getResources();
mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
+ mClosingFreeformWindowTransY =
+ res.getDimensionPixelSize(R.dimen.closing_freeform_window_trans_y);
mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius);
mLauncher.addOnDeviceProfileChangeListener(this);
@@ -1480,10 +1487,16 @@
? 0 : getWindowCornerRadius(mLauncher);
float startShadowRadius = areAllTargetsTranslucent(appTargets) ? 0 : mMaxShadowRadius;
closingAnimator.setDuration(duration);
+ boolean isFreeform = isFreeformAnimation(appTargets);
+ float translateY = isFreeform ? mClosingFreeformWindowTransY : mClosingWindowTransY;
+ float endScale = isFreeform ? 0.95f : 1f;
+ Interpolator alphaInterpolator = isFreeform
+ ? clampToDuration(LINEAR, 0, 100, duration)
+ : clampToDuration(LINEAR, 25, 125, duration);
closingAnimator.addUpdateListener(new MultiValueUpdateListener() {
- FloatProp mDy = new FloatProp(0, mClosingWindowTransY, DECELERATE_1_7);
- FloatProp mScale = new FloatProp(1f, 1f, DECELERATE_1_7);
- FloatProp mAlpha = new FloatProp(1f, 0f, clampToDuration(LINEAR, 25, 125, duration));
+ FloatProp mDy = new FloatProp(0, translateY, DECELERATE_1_7);
+ FloatProp mScale = new FloatProp(1f, endScale, DECELERATE_1_7);
+ FloatProp mAlpha = new FloatProp(1f, 0f, alphaInterpolator);
FloatProp mShadowRadius = new FloatProp(startShadowRadius, 0, DECELERATE_1_7);
@Override
@@ -1532,6 +1545,13 @@
return closingAnimator;
}
+ private boolean isFreeformAnimation(RemoteAnimationTarget[] appTargets) {
+ return DesktopModeStatus.canEnterDesktopMode(mLauncher.getApplicationContext())
+ && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS.isTrue()
+ && Arrays.stream(appTargets)
+ .anyMatch(app -> app.taskInfo != null && app.taskInfo.isFreeform());
+ }
+
private void addCujInstrumentation(Animator anim, int cuj) {
anim.addListener(getCujAnimationSuccessListener(cuj));
}
@@ -1726,8 +1746,21 @@
return new AnimatorBackState(rectFSpringAnim, anim);
}
- public static int getTaskbarToHomeDuration() {
- if (enableScalingRevealHomeAnimation()) {
+ /** Get animation duration for taskbar for going to home. */
+ public static int getTaskbarToHomeDuration(boolean isPinnedTaskbar) {
+ return getTaskbarToHomeDuration(false, isPinnedTaskbar);
+ }
+
+ /**
+ * Get animation duration for taskbar for going to home.
+ *
+ * @param shouldOverrideToFastAnimation should overwrite scaling reveal home animation duration
+ */
+ public static int getTaskbarToHomeDuration(boolean shouldOverrideToFastAnimation,
+ boolean isPinnedTaskbar) {
+ if (isPinnedTaskbar) {
+ return PINNED_TASKBAR_TRANSITION_DURATION;
+ } else if (enableScalingRevealHomeAnimation() && !shouldOverrideToFastAnimation) {
return TASKBAR_TO_HOME_DURATION_SLOW;
} else {
return TASKBAR_TO_HOME_DURATION_FAST;
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index bd2c7cc..dc0f899 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -55,6 +55,7 @@
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
+import com.android.systemui.animation.back.FlingOnBackAnimationCallback;
import java.util.ArrayList;
import java.util.HashSet;
@@ -374,12 +375,12 @@
/**
* Animation callback for different predictive back animation states for the widget picker.
*/
- private class BackAnimationCallback implements OnBackAnimationCallback {
+ private class BackAnimationCallback extends FlingOnBackAnimationCallback {
@Nullable
OnBackAnimationCallback mActiveOnBackAnimationCallback;
@Override
- public void onBackStarted(@NonNull BackEvent backEvent) {
+ public void onBackStartedCompat(@NonNull BackEvent backEvent) {
if (mActiveOnBackAnimationCallback != null) {
mActiveOnBackAnimationCallback.onBackCancelled();
}
@@ -390,7 +391,7 @@
}
@Override
- public void onBackInvoked() {
+ public void onBackInvokedCompat() {
if (mActiveOnBackAnimationCallback == null) {
return;
}
@@ -399,7 +400,7 @@
}
@Override
- public void onBackProgressed(@NonNull BackEvent backEvent) {
+ public void onBackProgressedCompat(@NonNull BackEvent backEvent) {
if (mActiveOnBackAnimationCallback == null) {
return;
}
@@ -407,7 +408,7 @@
}
@Override
- public void onBackCancelled() {
+ public void onBackCancelledCompat() {
if (mActiveOnBackAnimationCallback == null) {
return;
}
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 92d9516..8e80aa5 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -57,7 +57,7 @@
// Vertical padding of the icon that contributes to the expected cell height.
private final int mVerticalPadding;
// Extra padding that is used in the top app rows (prediction and search) that is not used in
- // the regular A-Z list. This only applies to single line label.
+ // the regular A-Z list.
private final int mTopRowExtraHeight;
// Helper to drawing the focus indicator.
@@ -140,7 +140,7 @@
// is not enabled. Otherwise, the extra height will increase by just the textHeight.
int extraHeight = (Flags.enableTwolineToggle() &&
LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(getContext()))
- ? textHeight : mTopRowExtraHeight;
+ ? (textHeight + mTopRowExtraHeight) : mTopRowExtraHeight;
totalHeight += extraHeight;
return getVisibility() == GONE ? 0 : totalHeight + getPaddingTop() + getPaddingBottom();
}
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
index 6916a1d..87a82f0 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
@@ -20,6 +20,7 @@
import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.content.Context
+import android.graphics.Rect
import android.os.IBinder
import android.view.SurfaceControl.Transaction
import android.view.WindowManager.TRANSIT_OPEN
@@ -31,6 +32,7 @@
import android.window.TransitionInfo.Change
import androidx.core.animation.addListener
import com.android.app.animation.Interpolators
+import com.android.internal.policy.ScreenDecorationsUtils
import com.android.quickstep.RemoteRunnable
import com.android.wm.shell.shared.animation.MinimizeAnimator
import com.android.wm.shell.shared.animation.WindowAnimator
@@ -43,8 +45,19 @@
* ([android.view.WindowManager.TRANSIT_TO_BACK]) this transition will apply a minimize animation to
* that window.
*/
-class DesktopAppLaunchTransition(private val context: Context, private val mainExecutor: Executor) :
- RemoteTransitionStub() {
+class DesktopAppLaunchTransition(
+ private val context: Context,
+ private val mainExecutor: Executor,
+ private val launchType: AppLaunchType,
+) : RemoteTransitionStub() {
+
+ enum class AppLaunchType(
+ val boundsAnimationParams: WindowAnimator.BoundsAnimationParams,
+ val alphaDurationMs: Long,
+ ) {
+ LAUNCH(launchBoundsAnimationDef, /* alphaDurationMs= */ 200L),
+ UNMINIMIZE(unminimizeBoundsAnimationDef, /* alphaDurationMs= */ 100L),
+ }
override fun startAnimation(
token: IBinder,
@@ -105,18 +118,24 @@
val boundsAnimator =
WindowAnimator.createBoundsAnimator(
context.resources.displayMetrics,
- launchBoundsAnimationDef,
+ launchType.boundsAnimationParams,
change,
transaction,
)
val alphaAnimator =
ValueAnimator.ofFloat(0f, 1f).apply {
- duration = LAUNCH_ANIM_ALPHA_DURATION_MS
+ duration = launchType.alphaDurationMs
interpolator = Interpolators.LINEAR
addUpdateListener { animation ->
transaction.setAlpha(change.leash, animation.animatedValue as Float).apply()
}
}
+ val clipRect = Rect(change.endAbsBounds).apply { offsetTo(0, 0) }
+ transaction.setCrop(change.leash, clipRect)
+ transaction.setCornerRadius(
+ change.leash,
+ ScreenDecorationsUtils.getWindowCornerRadius(context),
+ )
return AnimatorSet().apply {
playTogether(boundsAnimator, alphaAnimator)
addListener(onEnd = { animation -> onAnimFinish(animation) })
@@ -124,13 +143,19 @@
}
companion object {
- private val LAUNCH_CHANGE_MODES = intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT)
-
- private const val LAUNCH_ANIM_ALPHA_DURATION_MS = 100L
- private const val MINIMIZE_ANIM_ALPHA_DURATION_MS = 100L
+ /** Change modes that represent a task becoming visible / launching in Desktop mode. */
+ val LAUNCH_CHANGE_MODES = intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT)
private val launchBoundsAnimationDef =
WindowAnimator.BoundsAnimationParams(
+ durationMs = 600,
+ startOffsetYDp = 36f,
+ startScale = 0.95f,
+ interpolator = Interpolators.STANDARD_DECELERATE,
+ )
+
+ private val unminimizeBoundsAnimationDef =
+ WindowAnimator.BoundsAnimationParams(
durationMs = 300,
startOffsetYDp = 12f,
startScale = 0.97f,
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt
new file mode 100644
index 0000000..e32bcd1
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.desktop
+
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.content.Context
+import android.window.DesktopModeFlags
+import android.window.RemoteTransition
+import android.window.TransitionFilter
+import android.window.TransitionFilter.CONTAINER_ORDER_TOP
+import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.quickstep.SystemUiProxy
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+
+/** Manages transitions related to app launches in Desktop Mode. */
+class DesktopAppLaunchTransitionManager(
+ private val context: Context,
+ private val systemUiProxy: SystemUiProxy,
+) {
+ private var remoteWindowLimitUnminimizeTransition: RemoteTransition? = null
+
+ /**
+ * Register a [RemoteTransition] supporting Desktop app launches, and window limit
+ * minimizations.
+ */
+ fun registerTransitions() {
+ if (!shouldRegisterTransitions()) {
+ return
+ }
+ remoteWindowLimitUnminimizeTransition =
+ RemoteTransition(
+ DesktopAppLaunchTransition(context, MAIN_EXECUTOR, AppLaunchType.UNMINIMIZE)
+ )
+ systemUiProxy.registerRemoteTransition(
+ remoteWindowLimitUnminimizeTransition,
+ buildAppLaunchFilter(),
+ )
+ }
+
+ /**
+ * Unregister the [RemoteTransition] supporting Desktop app launches and window limit
+ * minimizations.
+ */
+ fun unregisterTransitions() {
+ if (!shouldRegisterTransitions()) {
+ return
+ }
+ systemUiProxy.unregisterRemoteTransition(remoteWindowLimitUnminimizeTransition)
+ remoteWindowLimitUnminimizeTransition = null
+ }
+
+ private fun shouldRegisterTransitions(): Boolean =
+ DesktopModeStatus.canEnterDesktopMode(context) &&
+ DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue
+
+ companion object {
+ private fun buildAppLaunchFilter(): TransitionFilter {
+ val openRequirement =
+ TransitionFilter.Requirement().apply {
+ mActivityType = ACTIVITY_TYPE_STANDARD
+ mWindowingMode = WINDOWING_MODE_FREEFORM
+ mModes = DesktopAppLaunchTransition.LAUNCH_CHANGE_MODES
+ mMustBeTask = true
+ mOrder = CONTAINER_ORDER_TOP
+ }
+ return TransitionFilter().apply {
+ mTypeSet = DesktopAppLaunchTransition.LAUNCH_CHANGE_MODES
+ mRequirements = arrayOf(openRequirement)
+ }
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index 5744464..fd0243a 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -488,6 +488,15 @@
}
});
}
+
+ public void onEnterDesktopModeTransitionStarted(int transitionDuration) {
+
+ }
+
+ @Override
+ public void onExitDesktopModeTransitionStarted(int transitionDuration) {
+
+ }
}
/** A listener for Taskbar in Desktop Mode. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index 23a5a27..3b7ad3e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -139,18 +139,42 @@
@NonNull Set<Integer> taskIdsToExclude,
boolean wasOpenedFromTaskbar) {
if (mQuickSwitchViewController != null) {
- if (!mQuickSwitchViewController.isCloseAnimationRunning()
- && mQuickSwitchViewController.wasOpenedFromTaskbar() == wasOpenedFromTaskbar) {
- return;
- }
+ if (!mQuickSwitchViewController.isCloseAnimationRunning()) {
+ if (mQuickSwitchViewController.wasOpenedFromTaskbar() == wasOpenedFromTaskbar) {
+ return;
+ }
- // Allow the KQS to be reopened during the close animation to make it more responsive.
- // Similarly, if KQS was opened in different mode (from taskbar vs. keyboard event),
- // close it so it can be reopened in the correct mode.
- // TODO(b/368119679) Consider updating list of shown tasks in place, or at least reopen
- // the view in the same vertical location.
- closeQuickSwitchView(false);
+ // Relayout the KQS view instead of recreating a new one if it is the current
+ // trigger surface is different than the previous one.
+ final int currentFocusIndexOverride =
+ currentFocusedIndex == -1 && !mControllerCallbacks.isFirstTaskRunning()
+ ? 0 : currentFocusedIndex;
+
+ // Skip the task reload if the list is not changed.
+ if (!mModel.isTaskListValid(mTaskListChangeId) || !taskIdsToExclude.equals(
+ mExcludedTaskIds)) {
+ mExcludedTaskIds = taskIdsToExclude;
+ mTaskListChangeId = mModel.getTasks((tasks) -> {
+ processLoadedTasks(tasks, taskIdsToExclude);
+ mQuickSwitchViewController.updateQuickSwitchView(
+ mTasks,
+ mNumHiddenTasks,
+ currentFocusIndexOverride,
+ mHasDesktopTask,
+ mWasDesktopTaskFilteredOut);
+ });
+ }
+
+ mQuickSwitchViewController.updateLayoutForSurface(wasOpenedFromTaskbar,
+ currentFocusIndexOverride);
+ return;
+ } else {
+ // Allow the KQS to be reopened during the close animation to make it more
+ // responsive.
+ closeQuickSwitchView(false);
+ }
}
+
mOverlayContext = mControllers.taskbarOverlayController.requestWindow();
if (Flags.taskbarOverflow()) {
mOverlayContext.getDragLayer().addTouchController(this);
@@ -186,13 +210,7 @@
mExcludedTaskIds = taskIdsToExclude;
mTaskListChangeId = mModel.getTasks((tasks) -> {
- mHasDesktopTask = false;
- mWasDesktopTaskFilteredOut = false;
- if (onDesktop) {
- processLoadedTasksOnDesktop(tasks, taskIdsToExclude);
- } else {
- processLoadedTasks(tasks, taskIdsToExclude);
- }
+ processLoadedTasks(tasks, taskIdsToExclude);
// Check if the first task is running after the recents model has updated so that we use
// the correct index.
mQuickSwitchViewController.openQuickSwitchView(
@@ -213,6 +231,17 @@
}
private void processLoadedTasks(List<GroupTask> tasks, Set<Integer> taskIdsToExclude) {
+ mHasDesktopTask = false;
+ mWasDesktopTaskFilteredOut = false;
+ if (mControllers.taskbarDesktopModeController.getAreDesktopTasksVisible()) {
+ processLoadedTasksOnDesktop(tasks, taskIdsToExclude);
+ } else {
+ processLoadedTasksOutsideDesktop(tasks, taskIdsToExclude);
+ }
+ }
+
+ private void processLoadedTasksOutsideDesktop(List<GroupTask> tasks,
+ Set<Integer> taskIdsToExclude) {
// Only store MAX_TASK tasks, from most to least recent
Collections.reverse(tasks);
mTasks = tasks.stream()
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index 05d34b5..1967dfd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -201,6 +201,8 @@
int currentFocusIndexOverride,
@NonNull KeyboardQuickSwitchViewController.ViewCallbacks viewCallbacks,
boolean useDesktopTaskView) {
+ mContent.removeAllViews();
+
mViewCallbacks = viewCallbacks;
Resources resources = context.getResources();
Resources.Theme theme = context.getTheme();
@@ -333,11 +335,17 @@
return closeAnimation;
}
- private void animateOpen(int currentFocusIndexOverride) {
+ protected void animateOpen(int currentFocusIndexOverride) {
if (mOpenAnimation != null) {
// Restart animation since currentFocusIndexOverride can change the initial scroll.
mOpenAnimation.cancel();
}
+
+ // Reset the alpha for the case where the KQS view is opened before.
+ setAlpha(0);
+ mScrollView.setAlpha(0);
+ mNoRecentItemsPane.setAlpha(0);
+
mOpenAnimation = new AnimatorSet();
Animator outlineAnimation = mOutlineAnimationProgress.animateToValue(1f);
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 5e11601..985cc26 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -36,6 +36,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.desktop.DesktopAppLaunchTransition;
+import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
import com.android.launcher3.util.DisplayController;
@@ -127,6 +128,23 @@
/* useDesktopTaskView= */ !onDesktop && hasDesktopTask);
}
+ protected void updateQuickSwitchView(
+ @NonNull List<GroupTask> tasks,
+ int numHiddenTasks,
+ int currentFocusIndexOverride,
+ boolean hasDesktopTask,
+ boolean wasDesktopTaskFilteredOut) {
+ mWasDesktopTaskFilteredOut = wasDesktopTaskFilteredOut;
+ mKeyboardQuickSwitchView.applyLoadPlan(
+ mOverlayContext,
+ tasks,
+ numHiddenTasks,
+ /* updateTasks= */ true,
+ currentFocusIndexOverride,
+ mViewCallbacks,
+ /* useDesktopTaskView= */ !mOnDesktop && hasDesktopTask);
+ }
+
protected void positionView(boolean wasOpenedFromTaskbar, boolean isTransientTaskbar) {
if (!wasOpenedFromTaskbar) {
// Keep the default positioning.
@@ -154,6 +172,20 @@
mKeyboardQuickSwitchView.setLayoutParams(lp);
}
+ protected void updateLayoutForSurface(boolean updateLayoutFromTaskbar,
+ int currentFocusIndexOverride) {
+ BaseDragLayer.LayoutParams lp =
+ (BaseDragLayer.LayoutParams) mKeyboardQuickSwitchView.getLayoutParams();
+
+ if (updateLayoutFromTaskbar) {
+ lp.width = BaseDragLayer.LayoutParams.WRAP_CONTENT;
+ } else {
+ lp.width = BaseDragLayer.LayoutParams.MATCH_PARENT;
+ }
+
+ mKeyboardQuickSwitchView.animateOpen(currentFocusIndexOverride);
+ }
+
boolean isCloseAnimationRunning() {
return mCloseAnimation != null;
}
@@ -251,7 +283,8 @@
) {
// This app is being unminimized - use our own transition runner.
remoteTransition = new RemoteTransition(
- new DesktopAppLaunchTransition(context, MAIN_EXECUTOR));
+ new DesktopAppLaunchTransition(
+ context, MAIN_EXECUTOR, AppLaunchType.UNMINIMIZE));
}
mControllers.taskbarActivityContext.handleGroupTaskLaunch(
task,
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 6454292..c5be13d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -33,6 +33,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
+import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
@@ -83,6 +84,7 @@
private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
dp -> {
onStashedInAppChanged(dp);
+ adjustHotseatForBubbleBar();
if (mControllers != null && mControllers.taskbarViewController != null) {
mControllers.taskbarViewController.onRotationChanged(dp);
}
@@ -211,8 +213,12 @@
}
private int getTaskbarAnimationDuration(boolean isVisible) {
- if (isVisible && !mLauncher.getPredictiveBackToHomeInProgress()) {
- return getTaskbarToHomeDuration();
+ // fast animation duration since we will not be playing workspace reveal animation.
+ boolean shouldOverrideToFastAnimation =
+ !isHotseatIconOnTopWhenAligned() || mLauncher.getPredictiveBackToHomeInProgress();
+ boolean isPinnedTaskbar = DisplayController.isPinnedTaskbar(mLauncher);
+ if (isVisible || isPinnedTaskbar) {
+ return getTaskbarToHomeDuration(shouldOverrideToFastAnimation, isPinnedTaskbar);
} else {
return DisplayController.isTransientTaskbar(mLauncher)
? TRANSIENT_TASKBAR_TRANSITION_DURATION
@@ -264,6 +270,14 @@
}
}
+ private void adjustHotseatForBubbleBar() {
+ Hotseat hotseat = mLauncher.getHotseat();
+ if (mControllers.bubbleControllers.isEmpty() || hotseat == null) return;
+ boolean hiddenForBubbles =
+ mControllers.bubbleControllers.get().bubbleBarViewController.isHiddenForNoBubbles();
+ hotseat.post(() -> adjustHotseatForBubbleBar(!hiddenForBubbles));
+ }
+
/**
* Create Taskbar animation when going from an app to Launcher as part of recents transition.
* @param toState If known, the state we will end up in when reaching Launcher.
@@ -363,16 +377,22 @@
// This method can be called before init() is called.
return;
}
- if (mControllers.uiController.isIconAlignedWithHotseat()
- && !mTaskbarLauncherStateController.isAnimatingToLauncher()) {
- // Only animate the nav buttons while home and not animating home, otherwise let
- // the TaskbarViewController handle it.
- mControllers.navbarButtonsViewController
- .getTaskbarNavButtonTranslationYForInAppDisplay()
- .updateValue(mLauncher.getDeviceProfile().getTaskbarOffsetY()
- * mTaskbarInAppDisplayProgress.value);
- mControllers.navbarButtonsViewController
- .getOnTaskbarBackgroundNavButtonColorOverride().updateValue(progress);
+ if (mControllers.uiController.isIconAlignedWithHotseat()) {
+ if (!mTaskbarLauncherStateController.isAnimatingToLauncher()) {
+ // Only animate the nav buttons while home and not animating home, otherwise let
+ // the TaskbarViewController handle it.
+ mControllers.navbarButtonsViewController
+ .getTaskbarNavButtonTranslationYForInAppDisplay()
+ .updateValue(mLauncher.getDeviceProfile().getTaskbarOffsetY()
+ * mTaskbarInAppDisplayProgress.value);
+ mControllers.navbarButtonsViewController
+ .getOnTaskbarBackgroundNavButtonColorOverride().updateValue(progress);
+ }
+ if (isBubbleBarEnabled()) {
+ mControllers.bubbleControllers.ifPresent(
+ c -> c.bubbleStashController.setInAppDisplayOverrideProgress(
+ mTaskbarInAppDisplayProgress.value));
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/ManageWindowsTaskbarShortcut.kt b/quickstep/src/com/android/launcher3/taskbar/ManageWindowsTaskbarShortcut.kt
new file mode 100644
index 0000000..c0c2a02
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/ManageWindowsTaskbarShortcut.kt
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.view.MotionEvent
+import android.view.View
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.R
+import com.android.launcher3.Utilities
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.popup.SystemShortcut
+import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_MULTI_INSTANCE_MENU_OPEN
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext
+import com.android.launcher3.util.Themes
+import com.android.launcher3.util.TouchController
+import com.android.launcher3.views.ActivityContext
+import com.android.quickstep.RecentsModel
+import com.android.quickstep.SystemUiProxy
+import com.android.quickstep.util.GroupTask
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer
+import java.util.Collections
+import java.util.function.Predicate
+
+/**
+ * A single menu item shortcut to execute displaying open instances of an app. Default interaction
+ * for [onClick] is to open the menu in a floating window. Touching one of the displayed tasks
+ * launches it.
+ */
+class ManageWindowsTaskbarShortcut<T>(
+ private val target: T,
+ private val itemInfo: ItemInfo?,
+ private val originalView: View?,
+ private val controllers: TaskbarControllers,
+) :
+ SystemShortcut<T>(
+ R.drawable.desktop_mode_ic_taskbar_menu_manage_windows,
+ R.string.manage_windows_option_taskbar,
+ target,
+ itemInfo,
+ originalView,
+ ) where T : Context?, T : ActivityContext? {
+ private lateinit var taskbarShortcutAllWindowsView: TaskbarShortcutManageWindowsView
+ private val recentsModel = RecentsModel.INSTANCE[controllers.taskbarActivityContext]
+
+ override fun onClick(v: View?) {
+ val filter =
+ Predicate<GroupTask> { task: GroupTask? ->
+ task != null && task.task1.key.packageName == itemInfo?.getTargetPackage()
+ }
+ recentsModel.getTasks(
+ { tasks: List<GroupTask> ->
+ // Since fetching thumbnails is asynchronous, use this set to gate until the tasks
+ // are ready to display
+ val pendingTaskIds =
+ Collections.synchronizedSet(tasks.map { it.task1.key.id }.toMutableSet())
+ createAndShowTaskShortcutView(tasks, pendingTaskIds)
+ },
+ filter,
+ )
+ }
+
+ /**
+ * Processes a list of tasks to generate thumbnails and create a taskbar shortcut view.
+ *
+ * Iterates through the tasks, retrieves thumbnails, and adds them to a list. When all
+ * thumbnails are processed, it creates a [TaskbarShortcutManageWindowsView] with the collected
+ * thumbnails and positions it appropriately.
+ */
+ private fun createAndShowTaskShortcutView(
+ tasks: List<GroupTask?>,
+ pendingTaskIds: MutableSet<Int>,
+ ) {
+ val taskList = arrayListOf<Pair<Int, Bitmap?>>()
+ tasks.forEach { groupTask ->
+ groupTask?.task1?.let { task ->
+ recentsModel.thumbnailCache.getThumbnailInBackground(task) {
+ thumbnailData: ThumbnailData ->
+ pendingTaskIds.remove(task.key.id)
+ // Add the current pair of task id and ThumbnailData to the list of all tasks
+ if (thumbnailData.thumbnail != null) {
+ taskList.add(task.key.id to thumbnailData.thumbnail)
+ }
+
+ // If the set is empty, all thumbnails have been fetched
+ if (pendingTaskIds.isEmpty() && taskList.isNotEmpty()) {
+ createAndPositionTaskbarShortcut(taskList)
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates and positions the [TaskbarShortcutManageWindowsView] with the provided thumbnails.
+ */
+ private fun createAndPositionTaskbarShortcut(taskList: ArrayList<Pair<Int, Bitmap?>>) {
+ val onIconClickListener =
+ ({ taskId: Int? ->
+ taskbarShortcutAllWindowsView.removeFromContainer()
+ if (taskId != null) {
+ SystemUiProxy.INSTANCE.get(target).showDesktopApp(taskId, null)
+ }
+ })
+
+ val onOutsideClickListener = { taskbarShortcutAllWindowsView.removeFromContainer() }
+
+ taskbarShortcutAllWindowsView =
+ TaskbarShortcutManageWindowsView(
+ originalView!!,
+ controllers.taskbarOverlayController.requestWindow(),
+ taskList,
+ onIconClickListener,
+ onOutsideClickListener,
+ controllers,
+ )
+ }
+
+ /**
+ * A view container for displaying the window of open instances of an app
+ *
+ * Handles showing the window snapshots, adding the carousel to the overlay, and closing it.
+ * Also acts as a touch controller to intercept touch events outside the carousel to close it.
+ */
+ class TaskbarShortcutManageWindowsView(
+ private val originalView: View,
+ private val taskbarOverlayContext: TaskbarOverlayContext,
+ snapshotList: ArrayList<Pair<Int, Bitmap?>>,
+ onIconClickListener: (Int) -> Unit,
+ onOutsideClickListener: () -> Unit,
+ private val controllers: TaskbarControllers,
+ ) :
+ ManageWindowsViewContainer(
+ originalView.context,
+ Themes.getAttrColor(originalView.context, R.attr.materialColorSurfaceBright),
+ ),
+ TouchController {
+ private val taskbarActivityContext = controllers.taskbarActivityContext
+
+ init {
+ createAndShowMenuView(snapshotList, onIconClickListener, onOutsideClickListener)
+ taskbarOverlayContext.dragLayer.addTouchController(this)
+ }
+
+ /** Adds the carousel menu to the taskbar overlay drag layer */
+ override fun addToContainer(menuView: ManageWindowsView) {
+ taskbarOverlayContext.dragLayer.post { positionCarouselMenu() }
+
+ controllers.taskbarAutohideSuspendController.updateFlag(
+ FLAG_AUTOHIDE_SUSPEND_MULTI_INSTANCE_MENU_OPEN,
+ true,
+ )
+ AbstractFloatingView.closeAllOpenViewsExcept(
+ taskbarActivityContext,
+ AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY,
+ )
+ menuView.rootView.minimumHeight = menuView.menuHeight
+ menuView.rootView.minimumWidth = menuView.menuWidth
+
+ taskbarOverlayContext.dragLayer?.addView(menuView.rootView)
+ menuView.rootView.requestFocus()
+ }
+
+ /**
+ * Positions the carousel menu relative to the taskbar and the calling app's icon.
+ *
+ * Calculates the Y position to place the carousel above the taskbar, and the X position to
+ * align with the calling app while ensuring it doesn't go beyond the screen edge.
+ */
+ private fun positionCarouselMenu() {
+ val margin =
+ context.resources.getDimension(
+ R.dimen.taskbar_multi_instance_menu_min_padding_from_screen_edge
+ )
+
+ // Calculate the Y position to place the carousel above the taskbar
+ val availableHeight = taskbarOverlayContext.dragLayer.height
+ menuView.rootView.y =
+ availableHeight -
+ menuView.menuHeight -
+ controllers.taskbarStashController.touchableHeight -
+ margin
+
+ // Calculate the X position to align with the calling app,
+ // but avoid clashing with the screen edge
+ val availableWidth = taskbarOverlayContext.dragLayer.width
+ if (Utilities.isRtl(context.resources)) {
+ menuView.rootView.translationX = -(availableWidth - menuView.menuWidth) / 2f
+ } else {
+ val maxX = availableWidth - menuView.menuWidth - margin
+ menuView.rootView.translationX = minOf(originalView.x, maxX)
+ }
+ }
+
+ /** Closes the carousel menu and removes it from the taskbar overlay drag layer */
+ override fun removeFromContainer() {
+ controllers.taskbarAutohideSuspendController.updateFlag(
+ FLAG_AUTOHIDE_SUSPEND_MULTI_INSTANCE_MENU_OPEN,
+ false,
+ )
+ controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true)
+ taskbarOverlayContext.dragLayer?.removeView(menuView.rootView)
+ taskbarOverlayContext.dragLayer.removeTouchController(this)
+ }
+
+ /** TouchController implementations for closing the carousel when touched outside */
+ override fun onControllerTouchEvent(ev: MotionEvent?): Boolean {
+ return false
+ }
+
+ override fun onControllerInterceptTouchEvent(ev: MotionEvent?): Boolean {
+ ev?.let {
+ if (
+ ev.action == MotionEvent.ACTION_DOWN &&
+ !taskbarOverlayContext.dragLayer.isEventOverView(menuView.rootView, ev)
+ ) {
+ removeFromContainer()
+ }
+ }
+ return false
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 7273fac..eb47bb0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -192,7 +192,9 @@
public void onDestroy() {
- mRegionSamplingHelper.stopAndDestroy();
+ if (mRegionSamplingHelper != null) {
+ mRegionSamplingHelper.stopAndDestroy();
+ }
mRegionSamplingHelper = null;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 14da79e..8149f81 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -59,6 +59,7 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
+import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.Process;
import android.os.Trace;
@@ -91,6 +92,7 @@
import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.desktop.DesktopAppLaunchTransition;
+import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
@@ -416,7 +418,7 @@
/** Called when the visibility of the bubble bar changed. */
public void bubbleBarVisibilityChanged(boolean isVisible) {
mControllers.uiController.adjustHotseatForBubbleBar(isVisible);
- mControllers.taskbarViewController.resetIconAlignmentController();
+ mControllers.taskbarViewController.adjustTaskbarForBubbleBar();
}
public void init(@NonNull TaskbarSharedState sharedState) {
@@ -864,6 +866,33 @@
return makeDefaultActivityOptions(SPLASH_SCREEN_STYLE_UNDEFINED);
}
+ private ActivityOptionsWrapper getActivityLaunchDesktopOptions(ItemInfo info) {
+ if (!DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue()) {
+ return null;
+ }
+ if (!areDesktopTasksVisible()) {
+ return null;
+ }
+ BubbleTextView.RunningAppState appState =
+ mControllers.taskbarRecentAppsController.getDesktopItemState(info);
+ AppLaunchType launchType = null;
+ switch (appState) {
+ case RUNNING:
+ return null;
+ case MINIMIZED:
+ launchType = AppLaunchType.UNMINIMIZE;
+ break;
+ case NOT_RUNNING:
+ launchType = AppLaunchType.LAUNCH;
+ break;
+ }
+ ActivityOptions options = ActivityOptions.makeRemoteTransition(
+ new RemoteTransition(
+ new DesktopAppLaunchTransition(
+ /* context= */ this, getMainExecutor(), launchType)));
+ return new ActivityOptionsWrapper(options, new RunnableList());
+ }
+
/**
* Sets a new data-source for this taskbar instance
*/
@@ -1404,7 +1433,9 @@
}
private RemoteTransition createUnminimizeRemoteTransition() {
- return new RemoteTransition(new DesktopAppLaunchTransition(this, getMainExecutor()));
+ return new RemoteTransition(
+ new DesktopAppLaunchTransition(
+ this, getMainExecutor(), AppLaunchType.UNMINIMIZE));
}
/**
@@ -1505,25 +1536,31 @@
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
- if (info.user.equals(Process.myUserHandle())) {
- // TODO(b/216683257): Use startActivityForResult for search results that require it.
- if (taskInRecents != null) {
- // Re launch instance from recents
- ActivityOptionsWrapper opts = getActivityLaunchOptions(null, info);
- opts.options.setLaunchDisplayId(
- getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
- if (ActivityManagerWrapper.getInstance()
- .startActivityFromRecents(taskInRecents.key, opts.options)) {
- mControllers.uiController.getRecentsView()
- .addSideTaskLaunchCallback(opts.onEndCallback);
- return;
- }
- }
- startActivity(intent);
- } else {
+ if (!info.user.equals(Process.myUserHandle())) {
+ // TODO b/376819104: support Desktop launch animations for apps in managed profiles
getSystemService(LauncherApps.class).startMainActivity(
intent.getComponent(), info.user, intent.getSourceBounds(), null);
+ return;
}
+ // TODO(b/216683257): Use startActivityForResult for search results that require it.
+ if (taskInRecents != null) {
+ // Re launch instance from recents
+ ActivityOptionsWrapper opts = getActivityLaunchOptions(null, info);
+ opts.options.setLaunchDisplayId(
+ getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
+ if (ActivityManagerWrapper.getInstance()
+ .startActivityFromRecents(taskInRecents.key, opts.options)) {
+ mControllers.uiController.getRecentsView()
+ .addSideTaskLaunchCallback(opts.onEndCallback);
+ return;
+ }
+ }
+ ActivityOptionsWrapper opts = null;
+ if (areDesktopTasksVisible()) {
+ opts = getActivityLaunchDesktopOptions(info);
+ }
+ Bundle optionsBundle = opts == null ? null : opts.options.toBundle();
+ startActivity(intent, optionsBundle);
} catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
.show();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
index 8ab2ffa..bdc7f92 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
@@ -47,6 +47,8 @@
public static final int FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR = 1 << 5;
// User has hovered the taskbar.
public static final int FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS = 1 << 6;
+ // User has multi instance window open.
+ public static final int FLAG_AUTOHIDE_SUSPEND_MULTI_INSTANCE_MENU_OPEN = 1 << 7;
@IntDef(flag = true, value = {
FLAG_AUTOHIDE_SUSPEND_FULLSCREEN,
@@ -56,6 +58,7 @@
FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER,
FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR,
FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
+ FLAG_AUTOHIDE_SUSPEND_MULTI_INSTANCE_MENU_OPEN,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AutohideSuspendFlag {}
@@ -133,6 +136,8 @@
"FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER");
appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR,
"FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR");
+ appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_MULTI_INSTANCE_MENU_OPEN,
+ "FLAG_AUTOHIDE_SUSPEND_MULTI_INSTANCE_MENU_OPEN");
return str.toString();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 5a63ca6..db70724 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -221,10 +221,13 @@
uiController = newUiController;
uiController.init(this);
uiController.updateStateForSysuiFlags(mSharedState.sysuiStateFlags);
- // if bubble controllers are present take bubble bar location, else set it to null
+ // if bubble controllers are present configure the UI controller
bubbleControllers.ifPresentOrElse(bubbleControllers -> {
BubbleBarLocation location =
bubbleControllers.bubbleBarViewController.getBubbleBarLocation();
+ boolean hiddenForBubbles =
+ bubbleControllers.bubbleBarViewController.isHiddenForNoBubbles();
+ uiController.adjustHotseatForBubbleBar(!hiddenForBubbles);
uiController.onBubbleBarLocationUpdated(location);
}, () -> uiController.onBubbleBarLocationUpdated(null));
// Notify that the ui controller has changed
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
index 8a86402..b7f5575 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
@@ -108,8 +108,10 @@
/** Clean up animations. */
public void onDestroy() {
startIconUndimming();
- mControllers.navbarButtonsViewController.setHomeButtonAccessibilityDelegate(null);
- mControllers.navbarButtonsViewController.setBackButtonAccessibilityDelegate(null);
+ if (mControllers != null) {
+ mControllers.navbarButtonsViewController.setHomeButtonAccessibilityDelegate(null);
+ mControllers.navbarButtonsViewController.setBackButtonAccessibilityDelegate(null);
+ }
}
private void startIconUndimming() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index fa04739..dce377d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -17,6 +17,7 @@
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.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_TASKBAR_ALIGNMENT;
import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_TASKBAR_STASH;
@@ -222,7 +223,9 @@
updateStateForFlag(FLAG_LAUNCHER_IN_STATE_TRANSITION, true);
if (!mShouldDelayLauncherStateAnim) {
if (toState == LauncherState.NORMAL) {
- applyState(QuickstepTransitionManager.getTaskbarToHomeDuration());
+ applyState(QuickstepTransitionManager.getTaskbarToHomeDuration(
+ DisplayController.isPinnedTaskbar(
+ mControllers.taskbarActivityContext)));
} else {
applyState();
}
@@ -459,9 +462,12 @@
private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) {
final boolean isInLauncher = isInLauncher();
+ final boolean isInOverview = mControllers.uiController.isInOverviewUi();
final boolean isIconAlignedWithHotseat = isIconAlignedWithHotseat();
final float toAlignment = isIconAlignedWithHotseat ? 1 : 0;
boolean handleOpenFloatingViews = false;
+ boolean isPinnedTaskbar = DisplayController.isPinnedTaskbar(
+ mControllers.taskbarActivityContext);
if (DEBUG) {
Log.d(TAG, "onStateChangeApplied - isInLauncher: " + isInLauncher
+ ", mLauncherState: " + mLauncherState
@@ -573,10 +579,17 @@
}
float backgroundAlpha = isInLauncher && isTaskbarAlignedWithHotseat() ? 0 : 1;
+ AnimatedFloat taskbarBgOffset =
+ mControllers.taskbarDragLayerController.getTaskbarBackgroundOffset();
+ boolean showTaskbar = !isInLauncher || isInOverview;
+ float taskbarBgOffsetEnd = showTaskbar ? 0f : 1f;
+ float taskbarBgOffsetStart = showTaskbar ? 1f : 0f;
// Don't animate if background has reached desired value.
if (mTaskbarBackgroundAlpha.isAnimating()
- || mTaskbarBackgroundAlpha.value != backgroundAlpha) {
+ || mTaskbarBackgroundAlpha.value != backgroundAlpha
+ || taskbarBgOffset.isAnimatingToValue(taskbarBgOffsetStart)
+ || taskbarBgOffset.value != taskbarBgOffsetEnd) {
mTaskbarBackgroundAlpha.cancelAnimation();
if (DEBUG) {
Log.d(TAG, "onStateChangeApplied - taskbarBackgroundAlpha - "
@@ -587,25 +600,35 @@
boolean isInLauncherIconNotAligned = isInLauncher && !isIconAlignedWithHotseat;
boolean notInLauncherIconNotAligned = !isInLauncher && !isIconAlignedWithHotseat;
boolean isInLauncherIconIsAligned = isInLauncher && isIconAlignedWithHotseat;
+ // When Hotseat icons are not on top don't change duration or add start delay.
+ // This will keep the duration in sync for icon alignment and background fade in/out.
+ // For example, launching app from launcher all apps.
+ boolean isHotseatIconOnTopWhenAligned =
+ mControllers.uiController.isHotseatIconOnTopWhenAligned();
float startDelay = 0;
// We want to delay the background from fading in so that the icons have time to move
// into the bounds of the background before it appears.
if (isInLauncherIconNotAligned) {
startDelay = duration * TASKBAR_BG_ALPHA_LAUNCHER_NOT_ALIGNED_DELAY_MULT;
- } else if (notInLauncherIconNotAligned) {
+ } else if (notInLauncherIconNotAligned && isHotseatIconOnTopWhenAligned) {
startDelay = duration * TASKBAR_BG_ALPHA_NOT_LAUNCHER_NOT_ALIGNED_DELAY_MULT;
}
float newDuration = duration - startDelay;
- if (isInLauncherIconIsAligned) {
+ if (isInLauncherIconIsAligned && isHotseatIconOnTopWhenAligned) {
// Make the background fade out faster so that it is gone by the time the
// icons move outside of the bounds of the background.
newDuration = duration * TASKBAR_BG_ALPHA_LAUNCHER_IS_ALIGNED_DURATION_MULT;
}
- Animator taskbarBackgroundAlpha = mTaskbarBackgroundAlpha
- .animateToValue(backgroundAlpha)
- .setDuration((long) newDuration);
- taskbarBackgroundAlpha.setStartDelay((long) startDelay);
+ Animator taskbarBackgroundAlpha = mTaskbarBackgroundAlpha.animateToValue(
+ backgroundAlpha);
+ if (isPinnedTaskbar) {
+ setupPinnedTaskbarAnimation(animatorSet, showTaskbar, taskbarBgOffset,
+ taskbarBgOffsetStart, taskbarBgOffsetEnd, duration, taskbarBackgroundAlpha);
+ } else {
+ taskbarBackgroundAlpha.setDuration((long) newDuration);
+ taskbarBackgroundAlpha.setStartDelay((long) startDelay);
+ }
animatorSet.play(taskbarBackgroundAlpha);
}
@@ -671,15 +694,18 @@
+ mIconAlignment.value
+ " -> " + toAlignment + ": " + duration);
}
- if (hasAnyFlag(FLAG_TASKBAR_HIDDEN)) {
- iconAlignAnim.setInterpolator(FINAL_FRAME);
- } else {
- animatorSet.play(iconAlignAnim);
+ if (!isPinnedTaskbar) {
+ if (hasAnyFlag(FLAG_TASKBAR_HIDDEN)) {
+ iconAlignAnim.setInterpolator(FINAL_FRAME);
+ } else {
+ animatorSet.play(iconAlignAnim);
+ }
}
}
- Interpolator interpolator = enableScalingRevealHomeAnimation()
+ Interpolator interpolator = enableScalingRevealHomeAnimation() && !isPinnedTaskbar
? ScalingWorkspaceRevealAnim.SCALE_INTERPOLATOR : EMPHASIZED;
+
animatorSet.setInterpolator(interpolator);
if (start) {
@@ -688,6 +714,49 @@
return animatorSet;
}
+ private void setupPinnedTaskbarAnimation(AnimatorSet animatorSet, boolean showTaskbar,
+ AnimatedFloat taskbarBgOffset, float taskbarBgOffsetStart, float taskbarBgOffsetEnd,
+ long duration, Animator taskbarBackgroundAlpha) {
+ float targetAlpha = !showTaskbar ? 1 : 0;
+ mLauncher.getHotseat().setIconsAlpha(targetAlpha, ALPHA_CHANNEL_TASKBAR_ALIGNMENT);
+ if (mIsQsbInline) {
+ mLauncher.getHotseat().setQsbAlpha(targetAlpha,
+ ALPHA_CHANNEL_TASKBAR_ALIGNMENT);
+ }
+
+ if ((taskbarBgOffset.value != taskbarBgOffsetEnd && !taskbarBgOffset.isAnimating())
+ || taskbarBgOffset.isAnimatingToValue(taskbarBgOffsetStart)) {
+ taskbarBgOffset.cancelAnimation();
+ Animator taskbarIconAlpha = mTaskbarAlphaForHome.animateToValue(
+ showTaskbar ? 1f : 0f);
+ AnimatedFloat taskbarIconTranslationYForHome =
+ mControllers.taskbarViewController.mTaskbarIconTranslationYForHome;
+ ObjectAnimator taskbarBackgroundOffset = taskbarBgOffset.animateToValue(
+ taskbarBgOffsetStart,
+ taskbarBgOffsetEnd);
+ ObjectAnimator taskbarIconsYTranslation = null;
+ float taskbarHeight =
+ mControllers.taskbarActivityContext.getDeviceProfile().taskbarHeight;
+ if (showTaskbar) {
+ taskbarIconsYTranslation = taskbarIconTranslationYForHome.animateToValue(
+ taskbarHeight, 0);
+ } else {
+ taskbarIconsYTranslation = taskbarIconTranslationYForHome.animateToValue(0,
+ taskbarHeight);
+ }
+
+ taskbarIconAlpha.setDuration(duration);
+ taskbarIconsYTranslation.setDuration(duration);
+ taskbarBackgroundOffset.setDuration(duration);
+
+ animatorSet.play(taskbarIconAlpha);
+ animatorSet.play(taskbarIconsYTranslation);
+ animatorSet.play(taskbarBackgroundOffset);
+ }
+ taskbarBackgroundAlpha.setInterpolator(showTaskbar ? INSTANT : FINAL_FRAME);
+ taskbarBackgroundAlpha.setDuration(duration);
+ }
+
/**
* Whether the taskbar is aligned with the hotseat in the current/target launcher state.
*
@@ -940,7 +1009,12 @@
@Override
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
- endGestureStateOverride(!controller.getFinishTargetIsLauncher(), false /*canceled*/);
+ endGestureStateOverride(!controller.getFinishTargetIsLauncher(),
+ controller.getLauncherIsVisibleAtFinish(), false /*canceled*/);
+ }
+
+ private void endGestureStateOverride(boolean finishedToApp, boolean canceled) {
+ endGestureStateOverride(finishedToApp, finishedToApp, canceled);
}
/**
@@ -950,10 +1024,13 @@
*
* @param finishedToApp {@code true} if the recents animation finished to showing an app and
* not workspace or overview
- * @param canceled {@code true} if the recents animation was canceled instead of finishing
- * to completion
+ * @param launcherIsVisible {code true} if launcher is visible at finish
+ * @param canceled {@code true} if the recents animation was canceled instead of
+ * finishing
+ * to completion
*/
- private void endGestureStateOverride(boolean finishedToApp, boolean canceled) {
+ private void endGestureStateOverride(boolean finishedToApp, boolean launcherIsVisible,
+ boolean canceled) {
mCallbacks.removeListener(this);
mTaskBarRecentsAnimationListener = null;
((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(null);
@@ -962,17 +1039,27 @@
mSkipNextRecentsAnimEnd = false;
return;
}
- updateStateForUserFinishedToApp(finishedToApp);
+ updateStateForUserFinishedToApp(finishedToApp, launcherIsVisible);
}
}
/**
- * Updates the visible state immediately to ensure a seamless handoff.
- * @param finishedToApp True iff user is in an app.
+ * @see #updateStateForUserFinishedToApp(boolean, boolean)
*/
private void updateStateForUserFinishedToApp(boolean finishedToApp) {
+ updateStateForUserFinishedToApp(finishedToApp, !finishedToApp);
+ }
+
+ /**
+ * Updates the visible state immediately to ensure a seamless handoff.
+ *
+ * @param finishedToApp True iff user is in an app.
+ * @param launcherIsVisible True iff launcher is still visible (ie. transparent app)
+ */
+ private void updateStateForUserFinishedToApp(boolean finishedToApp,
+ boolean launcherIsVisible) {
// Update the visible state immediately to ensure a seamless handoff
- boolean launcherVisible = !finishedToApp;
+ boolean launcherVisible = !finishedToApp || launcherIsVisible;
updateStateForFlag(FLAG_TRANSITION_TO_VISIBLE, false);
updateStateForFlag(FLAG_VISIBLE, launcherVisible);
applyState();
@@ -981,7 +1068,7 @@
if (DEBUG) {
Log.d(TAG, "endGestureStateOverride - FLAG_IN_APP: " + finishedToApp);
}
- controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
+ controller.updateStateForFlag(FLAG_IN_APP, finishedToApp && !launcherIsVisible);
controller.applyState();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 0f9ede9..d4764c7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -281,6 +281,10 @@
}
private void resetScreenUnpin() {
+ // if only back button was long pressed, navigate back like a single click back behavior.
+ if (mLongPressedButtons == BUTTON_BACK) {
+ executeBack(null);
+ }
mLongPressedButtons = 0;
mLastScreenPinLongPress = 0;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
index 126e9bb..712478e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
@@ -16,17 +16,28 @@
package com.android.launcher3.taskbar;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.BlendMode;
+import android.graphics.BlendModeColorFilter;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.IntProperty;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
+import androidx.core.graphics.ColorUtils;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.R;
import com.android.launcher3.Reorderable;
import com.android.launcher3.Utilities;
@@ -45,8 +56,104 @@
* each other in counter clockwise manner (icons of tasks partially overlapping with each other).
*/
public class TaskbarOverflowView extends FrameLayout implements Reorderable {
+ private static final int ALPHA_TRANSPARENT = 0;
+ private static final int ALPHA_OPAQUE = 255;
+ private static final long ANIMATION_DURATION_APPS_TO_LEAVE_BEHIND = 300L;
+ private static final long ANIMATION_DURATION_LEAVE_BEHIND_TO_APPS = 500L;
+ private static final long ANIMATION_SET_DURATION = 1000L;
+ private static final long ITEM_ICON_CENTER_OFFSET_ANIMATION_DURATION = 500L;
+ private static final long ITEM_ICON_COLOR_FILTER_OPACITY_ANIMATION_DURATION = 600L;
+ private static final long ITEM_ICON_SIZE_ANIMATION_DURATION = 500L;
+ private static final long ITEM_ICON_STROKE_WIDTH_ANIMATION_DURATION = 500L;
+ private static final long LEAVE_BEHIND_ANIMATIONS_DELAY = 500L;
+ private static final long LEAVE_BEHIND_OPACITY_ANIMATION_DURATION = 100L;
+ private static final long LEAVE_BEHIND_SIZE_ANIMATION_DURATION = 500L;
private static final int MAX_ITEMS_IN_PREVIEW = 4;
+ private static final FloatProperty<TaskbarOverflowView> ITEM_ICON_CENTER_OFFSET =
+ new FloatProperty<>("itemIconCenterOffset") {
+ @Override
+ public Float get(TaskbarOverflowView view) {
+ return view.mItemIconCenterOffset;
+ }
+
+ @Override
+ public void setValue(TaskbarOverflowView view, float value) {
+ view.mItemIconCenterOffset = value;
+ view.invalidate();
+ }
+ };
+
+ private static final IntProperty<TaskbarOverflowView> ITEM_ICON_COLOR_FILTER_OPACITY =
+ new IntProperty<>("itemIconColorFilterOpacity") {
+ @Override
+ public Integer get(TaskbarOverflowView view) {
+ return view.mItemIconColorFilterOpacity;
+ }
+
+ @Override
+ public void setValue(TaskbarOverflowView view, int value) {
+ view.mItemIconColorFilterOpacity = value;
+ view.invalidate();
+ }
+ };
+
+ private static final FloatProperty<TaskbarOverflowView> ITEM_ICON_SIZE =
+ new FloatProperty<>("itemIconSize") {
+ @Override
+ public Float get(TaskbarOverflowView view) {
+ return view.mItemIconSize;
+ }
+
+ @Override
+ public void setValue(TaskbarOverflowView view, float value) {
+ view.mItemIconSize = value;
+ view.invalidate();
+ }
+ };
+
+ private static final FloatProperty<TaskbarOverflowView> ITEM_ICON_STROKE_WIDTH =
+ new FloatProperty<>("itemIconStrokeWidth") {
+ @Override
+ public Float get(TaskbarOverflowView view) {
+ return view.mItemIconStrokeWidth;
+ }
+
+ @Override
+ public void setValue(TaskbarOverflowView view, float value) {
+ view.mItemIconStrokeWidth = value;
+ view.invalidate();
+ }
+ };
+
+ private static final IntProperty<TaskbarOverflowView> LEAVE_BEHIND_OPACITY =
+ new IntProperty<>("leaveBehindOpacity") {
+ @Override
+ public Integer get(TaskbarOverflowView view) {
+ return view.mLeaveBehindOpacity;
+ }
+
+ @Override
+ public void setValue(TaskbarOverflowView view, int value) {
+ view.mLeaveBehindOpacity = value;
+ view.invalidate();
+ }
+ };
+
+ private static final FloatProperty<TaskbarOverflowView> LEAVE_BEHIND_SIZE =
+ new FloatProperty<>("leaveBehindSize") {
+ @Override
+ public Float get(TaskbarOverflowView view) {
+ return view.mLeaveBehindSize;
+ }
+
+ @Override
+ public void setValue(TaskbarOverflowView view, float value) {
+ view.mLeaveBehindSize = value;
+ view.invalidate();
+ }
+ };
+
private boolean mIsRtlLayout;
private final List<Task> mItems = new ArrayList<Task>();
private int mIconSize;
@@ -56,11 +163,24 @@
private float mScaleForReorderBounce = 1f;
private int mItemBackgroundColor;
private int mLeaveBehindColor;
- private float mItemPreviewStrokeWidth;
// Active means the overflow icon has been pressed, which replaces the app icons with the
// leave-behind circle and shows the KQS UI.
private boolean mIsActive = false;
+ private ValueAnimator mStateTransitionAnimationWrapper;
+
+ private float mItemIconCenterOffsetDefault;
+ private float mItemIconCenterOffset; // [0..mItemIconCenterOffsetDefault]
+ private int mItemIconColorFilterOpacity; // [ALPHA_TRANSPARENT..ALPHA_OPAQUE]
+ private float mItemIconSizeDefault;
+ private float mItemIconSizeScaledDown;
+ private float mItemIconSize; // [mItemIconSizeScaledDown..mItemIconSizeDefault]
+ private float mItemIconStrokeWidthDefault;
+ private float mItemIconStrokeWidth; // [0..mItemIconStrokeWidthDefault]
+ private int mLeaveBehindOpacity; // [ALPHA_TRANSPARENT..ALPHA_OPAQUE]
+ private float mLeaveBehindSizeScaledDown;
+ private float mLeaveBehindSizeDefault;
+ private float mLeaveBehindSize; // [mLeaveBehindSizeScaledDown..mLeaveBehindSizeDefault]
public TaskbarOverflowView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -87,6 +207,12 @@
icon.mIconSize = iconSize;
icon.mPadding = padding;
+
+ final float radius = iconSize / 2f - padding;
+ final float size = radius + icon.mItemIconStrokeWidth;
+ icon.mItemIconCenterOffsetDefault = radius - size / 2 - icon.mItemIconStrokeWidth;
+ icon.mItemIconCenterOffset = icon.mItemIconCenterOffsetDefault;
+
return icon;
}
@@ -95,8 +221,22 @@
mItemBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mItemBackgroundColor = getContext().getColor(R.color.taskbar_background);
mLeaveBehindColor = Themes.getAttrColor(getContext(), android.R.attr.textColorTertiary);
- mItemPreviewStrokeWidth = getResources().getDimension(
- R.dimen.taskbar_overflow_button_preview_stroke);
+
+ mItemIconSizeDefault = getResources().getDimension(
+ R.dimen.taskbar_overflow_item_icon_size_default);
+ mItemIconSizeScaledDown = getResources().getDimension(
+ R.dimen.taskbar_overflow_item_icon_size_scaled_down);
+ mItemIconSize = mItemIconSizeDefault;
+
+ mItemIconStrokeWidthDefault = getResources().getDimension(
+ R.dimen.taskbar_overflow_item_icon_stroke_width_default);
+ mItemIconStrokeWidth = mItemIconStrokeWidthDefault;
+
+ mLeaveBehindSizeDefault = getResources().getDimension(
+ R.dimen.taskbar_overflow_leave_behind_size_default);
+ mLeaveBehindSizeScaledDown = getResources().getDimension(
+ R.dimen.taskbar_overflow_leave_behind_size_scaled_down);
+ mLeaveBehindSize = mLeaveBehindSizeScaledDown;
setWillNotDraw(false);
}
@@ -105,16 +245,14 @@
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
- if (mIsActive) {
- drawLeaveBehindCircle(canvas);
- } else {
- drawAppIcons(canvas);
- }
+ drawAppIcons(canvas);
+ drawLeaveBehindCircle(canvas);
}
private void drawAppIcons(@NonNull Canvas canvas) {
mItemBackgroundPaint.setColor(mItemBackgroundColor);
float radius = mIconSize / 2f - mPadding;
+ int adjustedItemIconSize = Math.round(mItemIconSize);
int itemsToShow = Math.min(mItems.size(), MAX_ITEMS_IN_PREVIEW);
for (int i = itemsToShow - 1; i >= 0; --i) {
@@ -123,36 +261,33 @@
continue;
}
- // Set the item icon size so two items fit within the overflow icon with stroke width
- // included, and overlap of 4 stroke width sizes between base item preview items.
- // 2 * strokeWidth + 2 * itemIconSize - 4 * strokeWidth = iconSize = 2 * radius.
- float itemIconSize = radius + mItemPreviewStrokeWidth;
- // Offset item icon from center so item icon stroke edge matches the parent icon edge.
- float itemCenterOffset = radius - itemIconSize / 2 - mItemPreviewStrokeWidth;
-
- float itemCenterX = getItemXOffset(itemCenterOffset, mIsRtlLayout, i, itemsToShow);
- float itemCenterY = getItemYOffset(itemCenterOffset, i, itemsToShow);
+ float itemCenterX = getItemXOffset(mItemIconCenterOffset, mIsRtlLayout, i, itemsToShow);
+ float itemCenterY = getItemYOffset(mItemIconCenterOffset, i, itemsToShow);
Drawable iconCopy = icon.getConstantState().newDrawable().mutate();
- iconCopy.setBounds(0, 0, (int) itemIconSize, (int) itemIconSize);
+ iconCopy.setBounds(0, 0, adjustedItemIconSize, adjustedItemIconSize);
+ iconCopy.setColorFilter(new BlendModeColorFilter(
+ ColorUtils.setAlphaComponent(mLeaveBehindColor, mItemIconColorFilterOpacity),
+ BlendMode.SRC_ATOP));
canvas.save();
- float itemIconRadius = itemIconSize / 2;
+ float itemIconRadius = adjustedItemIconSize / 2f;
canvas.translate(
mPadding + itemCenterX + radius - itemIconRadius,
mPadding + itemCenterY + radius - itemIconRadius);
canvas.drawCircle(itemIconRadius, itemIconRadius,
- itemIconRadius + mItemPreviewStrokeWidth, mItemBackgroundPaint);
+ itemIconRadius + mItemIconStrokeWidth, mItemBackgroundPaint);
iconCopy.draw(canvas);
canvas.restore();
}
}
private void drawLeaveBehindCircle(@NonNull Canvas canvas) {
- mItemBackgroundPaint.setColor(mLeaveBehindColor);
+ mItemBackgroundPaint.setColor(
+ ColorUtils.setAlphaComponent(mLeaveBehindColor, mLeaveBehindOpacity));
- final var xyCenter = mIconSize / 2f;
- canvas.drawCircle(xyCenter, xyCenter, mIconSize / 4f, mItemBackgroundPaint);
+ final float xyCenter = mIconSize / 2f;
+ canvas.drawCircle(xyCenter, xyCenter, mLeaveBehindSize / 2f, mItemBackgroundPaint);
}
/**
@@ -203,10 +338,98 @@
* @param isActive The next state of the view.
*/
public void setIsActive(boolean isActive) {
- if (mIsActive != isActive) {
- mIsActive = isActive;
- invalidate();
+ if (mIsActive == isActive) {
+ return;
}
+ mIsActive = isActive;
+
+ if (mStateTransitionAnimationWrapper != null
+ && mStateTransitionAnimationWrapper.isRunning()) {
+ mStateTransitionAnimationWrapper.reverse();
+ return;
+ }
+
+ final AnimatorSet stateTransitionAnimation = getStateTransitionAnimation();
+ mStateTransitionAnimationWrapper = ValueAnimator.ofFloat(0, 1f);
+ mStateTransitionAnimationWrapper.setDuration(mIsActive
+ ? ANIMATION_DURATION_APPS_TO_LEAVE_BEHIND
+ : ANIMATION_DURATION_LEAVE_BEHIND_TO_APPS);
+ mStateTransitionAnimationWrapper.setInterpolator(
+ mIsActive ? Interpolators.STANDARD : Interpolators.EMPHASIZED);
+ mStateTransitionAnimationWrapper.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mStateTransitionAnimationWrapper = null;
+ }
+ });
+ mStateTransitionAnimationWrapper.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ stateTransitionAnimation.setCurrentPlayTime(
+ (long) (ANIMATION_SET_DURATION * animator.getAnimatedFraction()));
+ }
+ });
+ mStateTransitionAnimationWrapper.start();
+ }
+
+ private AnimatorSet getStateTransitionAnimation() {
+ final AnimatorSet animation = new AnimatorSet();
+ animation.setInterpolator(Interpolators.LINEAR);
+ animation.playTogether(
+ buildAnimator(ITEM_ICON_CENTER_OFFSET, 0f, mItemIconCenterOffsetDefault,
+ ITEM_ICON_CENTER_OFFSET_ANIMATION_DURATION, 0L,
+ ITEM_ICON_CENTER_OFFSET_ANIMATION_DURATION),
+ buildAnimator(ITEM_ICON_COLOR_FILTER_OPACITY, ALPHA_OPAQUE, ALPHA_TRANSPARENT,
+ ITEM_ICON_COLOR_FILTER_OPACITY_ANIMATION_DURATION, 0L,
+ ANIMATION_SET_DURATION - ITEM_ICON_COLOR_FILTER_OPACITY_ANIMATION_DURATION),
+ buildAnimator(ITEM_ICON_SIZE, mItemIconSizeScaledDown, mItemIconSizeDefault,
+ ITEM_ICON_SIZE_ANIMATION_DURATION, 0L,
+ ITEM_ICON_SIZE_ANIMATION_DURATION),
+ buildAnimator(ITEM_ICON_STROKE_WIDTH, 0f, mItemIconStrokeWidthDefault,
+ ITEM_ICON_STROKE_WIDTH_ANIMATION_DURATION, 0L,
+ ITEM_ICON_STROKE_WIDTH_ANIMATION_DURATION),
+ buildAnimator(LEAVE_BEHIND_OPACITY, ALPHA_OPAQUE, ALPHA_TRANSPARENT,
+ LEAVE_BEHIND_OPACITY_ANIMATION_DURATION, LEAVE_BEHIND_ANIMATIONS_DELAY,
+ ANIMATION_SET_DURATION - LEAVE_BEHIND_ANIMATIONS_DELAY
+ - LEAVE_BEHIND_OPACITY_ANIMATION_DURATION),
+ buildAnimator(LEAVE_BEHIND_SIZE, mLeaveBehindSizeDefault,
+ mLeaveBehindSizeScaledDown, LEAVE_BEHIND_SIZE_ANIMATION_DURATION,
+ LEAVE_BEHIND_ANIMATIONS_DELAY, 0L)
+ );
+ return animation;
+ }
+
+ private ObjectAnimator buildAnimator(IntProperty<TaskbarOverflowView> property,
+ int finalValueWhenAnimatingToLeaveBehind, int finalValueWhenAnimatingToAppIcons,
+ long duration, long delayWhenAnimatingToLeaveBehind,
+ long delayWhenAnimatingToAppIcons) {
+ final ObjectAnimator animator = ObjectAnimator.ofInt(this, property,
+ mIsActive ? finalValueWhenAnimatingToLeaveBehind
+ : finalValueWhenAnimatingToAppIcons);
+ applyTiming(animator, duration, delayWhenAnimatingToLeaveBehind,
+ delayWhenAnimatingToAppIcons);
+ return animator;
+ }
+
+ private ObjectAnimator buildAnimator(FloatProperty<TaskbarOverflowView> property,
+ float finalValueWhenAnimatingToLeaveBehind, float finalValueWhenAnimatingToAppIcons,
+ long duration, long delayWhenAnimatingToLeaveBehind,
+ long delayWhenAnimatingToAppIcons) {
+ final ObjectAnimator animator = ObjectAnimator.ofFloat(this, property,
+ mIsActive ? finalValueWhenAnimatingToLeaveBehind
+ : finalValueWhenAnimatingToAppIcons);
+ applyTiming(animator, duration, delayWhenAnimatingToLeaveBehind,
+ delayWhenAnimatingToAppIcons);
+ return animator;
+ }
+
+ private void applyTiming(ObjectAnimator animator, long duration,
+ long delayWhenAnimatingToLeaveBehind,
+ long delayWhenAnimatingToAppIcons) {
+ animator.setDuration(duration);
+ animator.setStartDelay(
+ mIsActive ? delayWhenAnimatingToLeaveBehind : delayWhenAnimatingToAppIcons);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 70d4bb1..2e0bae5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -201,8 +201,10 @@
if (com.android.wm.shell.Flags.enableBubbleAnything()) {
shortcuts.add(BUBBLE);
}
+
if (Flags.enableMultiInstanceMenuTaskbar()
- && DesktopModeStatus.canEnterDesktopMode(mContext)) {
+ && DesktopModeStatus.canEnterDesktopMode(mContext)
+ && !mControllers.taskbarStashController.isInOverview()) {
shortcuts.addAll(getMultiInstanceMenuOptions().toList());
}
return shortcuts.stream();
@@ -295,9 +297,9 @@
* Returns a stream of Multi Instance menu options if an app supports it.
*/
Stream<SystemShortcut.Factory<BaseTaskbarContext>> getMultiInstanceMenuOptions() {
- SystemShortcut.Factory<BaseTaskbarContext> factory = createNewWindowShortcutFactory();
- return factory != null ? Stream.of(factory) : Stream.empty();
-
+ SystemShortcut.Factory<BaseTaskbarContext> f1 = createNewWindowShortcutFactory();
+ SystemShortcut.Factory<BaseTaskbarContext> f2 = createManageWindowsShortcutFactory();
+ return f1 != null ? Stream.of(f1, f2) : Stream.empty();
}
/**
@@ -317,6 +319,23 @@
}
/**
+ * Creates a factory function representing a "Manage Windows" menu item only if the calling app
+ * supports multi-instance. This menu item shows the open instances of the calling app.
+ * @return A factory function to be used in populating the long-press menu.
+ */
+ public SystemShortcut.Factory<BaseTaskbarContext> createManageWindowsShortcutFactory() {
+ return (context, itemInfo, originalView) -> {
+ ComponentKey key = itemInfo.getComponentKey();
+ AppInfo app = getApp(key);
+ if (app != null && app.supportsMultiInstance()) {
+ return new ManageWindowsTaskbarShortcut<>(context, itemInfo, originalView,
+ mControllers);
+ }
+ return null;
+ };
+ }
+
+ /**
* A single menu item ("Split left," "Split right," or "Split top") that executes a split
* from the taskbar, as if the user performed a drag and drop split.
* Includes an onClick method that initiates the actual split.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
index 7b05043..3d57de4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
@@ -73,6 +73,33 @@
var shownTasks: List<GroupTask> = emptyList()
private set
+ /**
+ * Returns the state of the most active Desktop task represented by the given [ItemInfo].
+ *
+ * If there are several tasks represented by the same [ItemInfo] we return the most active one,
+ * i.e. we return [DesktopAppState.RUNNING] over [DesktopAppState.MINIMIZED], and
+ * [DesktopAppState.MINIMIZED] over [DesktopAppState.NOT_RUNNING].
+ */
+ fun getDesktopItemState(itemInfo: ItemInfo?): RunningAppState {
+ val packageName = itemInfo?.getTargetPackage() ?: return RunningAppState.NOT_RUNNING
+ return getDesktopAppState(packageName, itemInfo.user.identifier)
+ }
+
+ private fun getDesktopAppState(packageName: String, userId: Int): RunningAppState {
+ val tasks = desktopTask?.tasks ?: return RunningAppState.NOT_RUNNING
+ val appTasks =
+ tasks.filter { task ->
+ packageName == task.key.packageName && task.key.userId == userId
+ }
+ if (appTasks.find { getRunningAppState(it.key.id) == RunningAppState.RUNNING } != null) {
+ return RunningAppState.RUNNING
+ }
+ if (appTasks.find { getRunningAppState(it.key.id) == RunningAppState.MINIMIZED } != null) {
+ return RunningAppState.MINIMIZED
+ }
+ return RunningAppState.NOT_RUNNING
+ }
+
/** Get the [RunningAppState] for the given task. */
fun getRunningAppState(taskId: Int): RunningAppState {
return when (taskId) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index c1dd216..67be8da 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -23,6 +23,7 @@
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.internal.jank.InteractionJankMonitor.Configuration;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
+import static com.android.launcher3.QuickstepTransitionManager.PINNED_TASKBAR_TRANSITION_DURATION;
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;
@@ -398,6 +399,9 @@
* Returns how long the stash/unstash animation should play.
*/
public long getStashDuration() {
+ if (DisplayController.isPinnedTaskbar(mActivity)) {
+ return PINNED_TASKBAR_TRANSITION_DURATION;
+ }
return DisplayController.isTransientTaskbar(mActivity)
? TRANSIENT_TASKBAR_STASH_DURATION
: TASKBAR_STASH_DURATION;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 55bcb23..8816a6d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -120,7 +120,8 @@
private boolean mShouldTryStartAlign;
- private final int mMaxNumIcons;
+ private int mMaxNumIcons = 0;
+ private int mIdealNumIcons = 0;
private final int mAllAppsButtonTranslationOffset;
@@ -188,8 +189,6 @@
// TODO: Disable touch events on QSB otherwise it can crash.
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
-
- mMaxNumIcons = calculateMaxNumIcons();
}
/**
@@ -200,11 +199,15 @@
int availableWidth = deviceProfile.widthPx;
int defaultEdgeMargin =
(int) getResources().getDimension(deviceProfile.inv.inlineNavButtonsEndSpacing);
+ int spaceForBubbleBar =
+ Math.round(mControllerCallbacks.getBubbleBarMaxCollapsedWidthIfVisible());
// Reserve space required for edge margins, or for navbar if shown. If task bar needs to be
// center aligned with nav bar shown, reserve space on both sides.
- availableWidth -= Math.max(defaultEdgeMargin, deviceProfile.hotseatBarEndOffset);
- availableWidth -= Math.max(defaultEdgeMargin,
+ availableWidth -=
+ Math.max(defaultEdgeMargin + spaceForBubbleBar, deviceProfile.hotseatBarEndOffset);
+ availableWidth -= Math.max(
+ defaultEdgeMargin + (mShouldTryStartAlign ? 0 : spaceForBubbleBar),
mShouldTryStartAlign ? 0 : deviceProfile.hotseatBarEndOffset);
// The space taken by an item icon used during layout.
@@ -231,6 +234,21 @@
return Math.floorDiv(availableWidth, iconSize) + additionalIcons;
}
+ /**
+ * Recalculates the max number of icons the taskbar view can show without entering overflow.
+ * Returns whether the max number of icons changed and the change affects the number of icons
+ * that should be shown in the taskbar.
+ */
+ boolean updateMaxNumIcons() {
+ if (!Flags.taskbarOverflow()) {
+ return false;
+ }
+ int oldMaxNumIcons = mMaxNumIcons;
+ mMaxNumIcons = calculateMaxNumIcons();
+ return oldMaxNumIcons != mMaxNumIcons
+ && (mIdealNumIcons > oldMaxNumIcons || mIdealNumIcons > mMaxNumIcons);
+ }
+
@Override
public void setVisibility(int visibility) {
boolean changed = getVisibility() != visibility;
@@ -328,6 +346,10 @@
&& mActivityContext.getTaskbarFeatureEvaluator().getSupportsPinningPopup()) {
setOnTouchListener(mControllerCallbacks.getTaskbarTouchListener());
}
+
+ if (Flags.taskbarOverflow()) {
+ mMaxNumIcons = calculateMaxNumIcons();
+ }
}
private void removeAndRecycle(View view) {
@@ -460,8 +482,9 @@
}
}
- overflowSize =
- nextViewIndex + numberOfSupportedRecents + nonTaskIconsToBeAdded - mMaxNumIcons;
+ mIdealNumIcons = nextViewIndex + numberOfSupportedRecents + nonTaskIconsToBeAdded;
+ overflowSize = mIdealNumIcons - mMaxNumIcons;
+
if (overflowSize > 0 && mTaskbarOverflowView != null) {
addView(mTaskbarOverflowView, nextViewIndex++);
} else if (mTaskbarOverflowView != null) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
index 834f92e..f65f307 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
@@ -137,6 +137,17 @@
return null;
}
+ /**
+ * Get the max bubble bar collapsed width for the current bubble bar visibility state. Used to
+ * reserve space for the bubble bar when transitioning taskbar view into overflow.
+ */
+ public float getBubbleBarMaxCollapsedWidthIfVisible() {
+ return mControllers.bubbleControllers
+ .filter(c -> !c.bubbleBarViewController.isHiddenForNoBubbles())
+ .map(c -> c.bubbleBarViewController.getCollapsedWidthWithMaxVisibleBubbles())
+ .orElse(0f);
+ }
+
/** Returns true if bubble bar controllers present and enabled in persistent taskbar. */
public boolean isBubbleBarEnabledInPersistentTaskbar() {
return Flags.enableBubbleBarInPersistentTaskBar()
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 494c472..bb4f07a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -120,7 +120,7 @@
private final TaskbarView mTaskbarView;
private final MultiValueAlpha mTaskbarIconAlpha;
private final AnimatedFloat mTaskbarIconScaleForStash = new AnimatedFloat(this::updateScale);
- private final AnimatedFloat mTaskbarIconTranslationYForHome = new AnimatedFloat(
+ public final AnimatedFloat mTaskbarIconTranslationYForHome = new AnimatedFloat(
this::updateTranslationY);
private final AnimatedFloat mTaskbarIconTranslationYForStash = new AnimatedFloat(
this::updateTranslationY);
@@ -779,9 +779,16 @@
}
}
- /** Resets the icon alignment controller so that it can be recreated again later. */
- void resetIconAlignmentController() {
+ /**
+ * Resets the icon alignment controller so that it can be recreated again later, and updates
+ * the list of icons shown in the taskbar if the bubble bar visibility changes the taskbar
+ * overflow state.
+ */
+ void adjustTaskbarForBubbleBar() {
mIconAlignControllerLazy = null;
+ if (mTaskbarView.updateMaxNumIcons()) {
+ commitRunningAppsToUI();
+ }
}
/**
@@ -789,6 +796,8 @@
*/
private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) {
PendingAnimation setter = new PendingAnimation(100);
+ // icon alignment not needed for pinned taskbar.
+ if (DisplayController.isPinnedTaskbar(mActivity)) return setter.createPlaybackController();
mOnControllerPreCreateCallback.run();
DeviceProfile taskbarDp = mActivity.getDeviceProfile();
Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 334ba6e..e0814d3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -417,8 +417,13 @@
// Updates mean the dot state may have changed; any other changes were updated in
// the populateBubble step.
BubbleBarBubble bb = mBubbles.get(update.updatedBubble.getKey());
- mBubbleBarViewController.animateBubbleNotification(
- bb, /* isExpanding= */ false, /* isUpdate= */ true);
+ if (suppressAnimation) {
+ // since we're not animating this update, we should update the dot visibility here.
+ bb.getView().updateDotVisibility(/* animate= */ false);
+ } else {
+ mBubbleBarViewController.animateBubbleNotification(
+ bb, /* isExpanding= */ false, /* isUpdate= */ true);
+ }
}
if (update.bubbleKeysInOrder != null && !update.bubbleKeysInOrder.isEmpty()) {
// Create the new list
@@ -523,9 +528,10 @@
* <p>
* Updates the value locally in Launcher and in WMShell.
*/
- public void updateBubbleBarLocation(BubbleBarLocation location) {
+ public void updateBubbleBarLocation(BubbleBarLocation location,
+ @BubbleBarLocation.UpdateSource int source) {
updateBubbleBarLocationInternal(location);
- mSystemUiProxy.setBubbleBarLocation(location);
+ mSystemUiProxy.setBubbleBarLocation(location, source);
}
private void updateBubbleBarLocationInternal(BubbleBarLocation location) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index c0a76a8..c5c2d69 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -407,11 +407,13 @@
return true;
}
if (action == R.id.action_move_left) {
- mController.updateBubbleBarLocation(BubbleBarLocation.LEFT);
+ mController.updateBubbleBarLocation(BubbleBarLocation.LEFT,
+ BubbleBarLocation.UpdateSource.A11Y_ACTION_BAR);
return true;
}
if (action == R.id.action_move_right) {
- mController.updateBubbleBarLocation(BubbleBarLocation.RIGHT);
+ mController.updateBubbleBarLocation(BubbleBarLocation.RIGHT,
+ BubbleBarLocation.UpdateSource.A11Y_ACTION_BAR);
return true;
}
return false;
@@ -1288,10 +1290,14 @@
// If there are more than 2 bubbles, the first 2 should be visible when collapsed,
// excluding the overflow.
return bubbleChildCount >= MAX_VISIBLE_BUBBLES_COLLAPSED
- ? getScaledIconSize() + mIconOverlapAmount + horizontalPadding
+ ? getCollapsedWidthWithMaxVisibleBubbles()
: getScaledIconSize() + horizontalPadding;
}
+ float getCollapsedWidthWithMaxVisibleBubbles() {
+ return getScaledIconSize() + mIconOverlapAmount + 2 * mBubbleBarPadding;
+ }
+
/** Returns the child count excluding the overflow if it's present. */
int getBubbleChildCount() {
return hasOverflow() ? getChildCount() - 1 : getChildCount();
@@ -1567,6 +1573,7 @@
void dismissBubbleBar();
/** Requests the controller to update bubble bar location to the given value */
- void updateBubbleBarLocation(BubbleBarLocation location);
+ void updateBubbleBarLocation(BubbleBarLocation location,
+ @BubbleBarLocation.UpdateSource int source);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 96fadf7..d842138 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -217,8 +217,9 @@
}
@Override
- public void updateBubbleBarLocation(BubbleBarLocation location) {
- mBubbleBarController.updateBubbleBarLocation(location);
+ public void updateBubbleBarLocation(BubbleBarLocation location,
+ @BubbleBarLocation.UpdateSource int source) {
+ mBubbleBarController.updateBubbleBarLocation(location, source);
}
});
@@ -242,8 +243,9 @@
}
@Override
- public void updateBubbleBarLocation(BubbleBarLocation location) {
- mBubbleBarController.updateBubbleBarLocation(location);
+ public void updateBubbleBarLocation(BubbleBarLocation location,
+ @BubbleBarLocation.UpdateSource int source) {
+ mBubbleBarController.updateBubbleBarLocation(location, source);
}
};
}
@@ -471,6 +473,13 @@
}
/**
+ * @return the max collapsed width for the bubble bar.
+ */
+ public float getCollapsedWidthWithMaxVisibleBubbles() {
+ return mBarView.getCollapsedWidthWithMaxVisibleBubbles();
+ }
+
+ /**
* @return {@code true} if bubble bar is on the left edge of the screen, {@code false} if on
* the right
*/
@@ -893,9 +902,10 @@
mBubbleBarViewAnimator.animateToInitialState(bubble, isInApp, isExpanding);
return;
}
- boolean persistentTaskbarOrOnHome = mBubbleStashController.isBubblesShowingOnHome()
+ // if we're not stashed or we're in persistent taskbar, animate for collapsed state.
+ boolean animateForCollapsed = !mBubbleStashController.isStashed()
|| !mBubbleStashController.isTransientTaskBar();
- if (persistentTaskbarOrOnHome) {
+ if (animateForCollapsed) {
mBubbleBarViewAnimator.animateBubbleBarForCollapsed(bubble, isExpanding);
return;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index 42bd197..fd4cf0e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -161,7 +161,8 @@
@Override
void onDragEnd() {
- mBubbleBarController.updateBubbleBarLocation(mReleasedLocation);
+ mBubbleBarController.updateBubbleBarLocation(mReleasedLocation,
+ BubbleBarLocation.UpdateSource.DRAG_BUBBLE);
mBubbleBarViewController.onBubbleDragEnd();
mBubblePinController.setListener(null);
}
@@ -226,7 +227,8 @@
@Override
void onDragEnd() {
// Make sure to update location as the first thing. Pivot update causes a relayout
- mBubbleBarController.updateBubbleBarLocation(mReleasedLocation);
+ mBubbleBarController.updateBubbleBarLocation(mReleasedLocation,
+ BubbleBarLocation.UpdateSource.DRAG_BAR);
bubbleBarView.setIsDragging(false);
// Restoring the initial pivot for the bubble bar view
bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index 0ea4222..c74fa9b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -224,12 +224,14 @@
}
if (action == R.id.action_move_left) {
if (mController != null) {
- mController.updateBubbleBarLocation(BubbleBarLocation.LEFT);
+ mController.updateBubbleBarLocation(BubbleBarLocation.LEFT,
+ BubbleBarLocation.UpdateSource.A11Y_ACTION_BUBBLE);
}
}
if (action == R.id.action_move_right) {
if (mController != null) {
- mController.updateBubbleBarLocation(BubbleBarLocation.RIGHT);
+ mController.updateBubbleBarLocation(BubbleBarLocation.RIGHT,
+ BubbleBarLocation.UpdateSource.A11Y_ACTION_BUBBLE);
}
}
return false;
@@ -483,6 +485,7 @@
void collapse();
/** Request bubble bar location to be updated to the given location */
- void updateBubbleBarLocation(BubbleBarLocation location);
+ void updateBubbleBarLocation(BubbleBarLocation location,
+ @BubbleBarLocation.UpdateSource int source);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
index 7b20eea..908e97c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
@@ -21,6 +21,7 @@
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.core.animation.ValueAnimator
+import com.android.app.animation.InterpolatorsAndroidX
import com.android.launcher3.R
import com.android.systemui.util.addListener
@@ -35,7 +36,8 @@
) {
private companion object {
- const val ANIMATION_DURATION_MS = 250L
+ const val EXPAND_ANIMATION_DURATION_MS = 400L
+ const val COLLAPSE_ANIMATION_DURATION_MS = 350L
}
private var flyout: BubbleBarFlyoutView? = null
@@ -86,9 +88,10 @@
private fun showFlyout(animationType: AnimationType, endAction: () -> Unit) {
val flyout = this.flyout ?: return
val startValue = getCurrentAnimatedValueIfRunning() ?: 0f
- val duration = (ANIMATION_DURATION_MS * (1f - startValue)).toLong()
+ val duration = (EXPAND_ANIMATION_DURATION_MS * (1f - startValue)).toLong()
animator?.cancel()
val animator = ValueAnimator.ofFloat(startValue, 1f).setDuration(duration)
+ animator.interpolator = InterpolatorsAndroidX.EMPHASIZED
this.animator = animator
when (animationType) {
AnimationType.FADE ->
@@ -111,6 +114,7 @@
fun updateFlyoutFullyExpanded(message: BubbleBarFlyoutMessage, onEnd: () -> Unit) {
val flyout = flyout ?: return
hideFlyout(AnimationType.FADE) {
+ callbacks.resetTopBoundary()
flyout.updateData(message) { showFlyout(AnimationType.FADE, onEnd) }
}
}
@@ -152,9 +156,10 @@
private fun hideFlyout(animationType: AnimationType, endAction: () -> Unit) {
val flyout = this.flyout ?: return
val startValue = getCurrentAnimatedValueIfRunning() ?: 1f
- val duration = (ANIMATION_DURATION_MS * startValue).toLong()
+ val duration = (COLLAPSE_ANIMATION_DURATION_MS * startValue).toLong()
animator?.cancel()
val animator = ValueAnimator.ofFloat(startValue, 0f).setDuration(duration)
+ animator.interpolator = InterpolatorsAndroidX.EMPHASIZED
this.animator = animator
when (animationType) {
AnimationType.FADE ->
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
index 418675c..f9f5a15 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
@@ -35,6 +35,7 @@
import androidx.core.animation.ArgbEvaluator
import com.android.launcher3.R
import com.android.launcher3.popup.RoundedArrowDrawable
+import kotlin.math.min
/** The flyout view used to notify the user of a new bubble notification. */
class BubbleBarFlyoutView(
@@ -46,6 +47,8 @@
private companion object {
// the minimum progress of the expansion animation before the content starts fading in.
const val MIN_EXPANSION_PROGRESS_FOR_CONTENT_ALPHA = 0.75f
+ // the rate multiple for the background color animation relative to the morph animation.
+ const val BACKGROUND_COLOR_CHANGE_RATE = 5
}
private val scheduler: FlyoutScheduler = scheduler ?: HandlerScheduler(this)
@@ -204,6 +207,8 @@
minExpansionProgressForTriangle =
positioner.distanceToRevealTriangle / translationToCollapsedPosition.y
+ backgroundPaint.color = collapsedColor
+
// post the request to start the expand animation to the looper so the view can measure
// itself
scheduler.runAfterLayout(expandAnimation)
@@ -307,8 +312,16 @@
height.toFloat() - triangleHeight + triangleOverlap,
)
+ // transform the flyout color between the collapsed and expanded states. the color
+ // transformation completes at a faster rate (BACKGROUND_COLOR_CHANGE_RATE) than the
+ // expansion animation. this helps make the color change smooth.
backgroundPaint.color =
- ArgbEvaluator.getInstance().evaluate(expansionProgress, collapsedColor, backgroundColor)
+ ArgbEvaluator.getInstance()
+ .evaluate(
+ min(expansionProgress * BACKGROUND_COLOR_CHANGE_RATE, 1f),
+ collapsedColor,
+ backgroundColor,
+ )
canvas.save()
canvas.translate(backgroundRectTx, backgroundRectTy)
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
index 831faa1..d9589bb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
@@ -176,12 +176,18 @@
bubbleBarTranslationYForTaskbar
}
- /** Translation Y to align the bubble bar with the hotseat. */
+ /** Translation Y to align the bubble bar with the taskbar. */
val bubbleBarTranslationYForTaskbar: Float
- /** Return translation Y to align the bubble bar with the taskbar. */
+ /** Return translation Y to align the bubble bar with the hotseat. */
val bubbleBarTranslationYForHotseat: Float
+ /**
+ * Show bubble bar is if it were in-app while launcher state is still on home. Set as a progress
+ * value between 0 and 1: 0 - use home layout, 1 - use in-app layout.
+ */
+ var inAppDisplayOverrideProgress: Float
+
/** Dumps the state of BubbleStashController. */
fun dump(pw: PrintWriter) {
pw.println("Bubble stash controller state:")
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
index c117ad4..45f5568 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
@@ -22,6 +22,8 @@
import android.graphics.Rect
import android.view.MotionEvent
import android.view.View
+import com.android.app.animation.Interpolators
+import com.android.launcher3.Utilities
import com.android.launcher3.anim.AnimatedFloat
import com.android.launcher3.taskbar.TaskbarInsetsController
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
@@ -52,7 +54,9 @@
if (field == state) return
val transitionFromHome = field == BubbleLauncherState.HOME
field = state
- if (!bubbleBarViewController.hasBubbles()) {
+ val hasBubbles = bubbleBarViewController.hasBubbles()
+ bubbleBarViewController.onBubbleBarConfigurationChanged(hasBubbles)
+ if (!hasBubbles) {
// if there are no bubbles, there's nothing to show, so just return.
return
}
@@ -63,7 +67,6 @@
// on home but in persistent taskbar elsewhere so the position is different.
animateBubbleBarY()
}
- bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
}
override var isSysuiLocked: Boolean = false
@@ -97,6 +100,37 @@
return -hotseatVerticalCenter + bubbleBarHeight / 2
}
+ override val bubbleBarTranslationY: Float
+ get() =
+ if (inAppDisplayOverrideProgress > 0f && launcherState == BubbleLauncherState.HOME) {
+ Utilities.mapToRange(
+ inAppDisplayOverrideProgress,
+ /* fromMin = */ 0f,
+ /* fromMax = */ 1f,
+ bubbleBarTranslationYForHotseat,
+ bubbleBarTranslationYForTaskbar,
+ Interpolators.LINEAR,
+ )
+ } else {
+ super.bubbleBarTranslationY
+ }
+
+ override var inAppDisplayOverrideProgress: Float = 0f
+ set(value) {
+ if (field == value) return
+ field = value
+ if (launcherState == BubbleLauncherState.HOME) {
+ if (bubbleBarTranslationYAnimator.isAnimating) {
+ bubbleBarTranslationYAnimator.cancelAnimation()
+ }
+ bubbleBarTranslationYAnimator.updateValue(bubbleBarTranslationY)
+ if (value == 0f || value == 1f) {
+ // Update insets only when we reach the end values
+ taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+ }
+ }
+
override fun init(
taskbarInsetsController: TaskbarInsetsController,
bubbleBarViewController: BubbleBarViewController,
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
index fbeecaa..e62c0d4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
@@ -87,7 +87,9 @@
set(state) {
if (field == state) return
field = state
- if (!bubbleBarViewController.hasBubbles()) {
+ val hasBubbles = bubbleBarViewController.hasBubbles()
+ bubbleBarViewController.onBubbleBarConfigurationChanged(hasBubbles)
+ if (!hasBubbles) {
// if there are no bubbles, there's nothing to show, so just return.
return
}
@@ -103,7 +105,6 @@
// Only stash if we're in an app, otherwise we're in home or overview where we should
// be un-stashed
updateStashedAndExpandedState(field == BubbleLauncherState.IN_APP, expand = false)
- bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
}
override var isSysuiLocked: Boolean = false
@@ -126,6 +127,9 @@
override val bubbleBarTranslationYForTaskbar: Float =
-taskbarHotseatDimensionsProvider.getTaskbarBottomSpace().toFloat()
+ /** Not supported in transient mode */
+ override var inAppDisplayOverrideProgress: Float = 0f
+
/** Check if we have handle view controller */
override val hasHandleView: Boolean
get() = bubbleStashedHandleViewController != null
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
index c5f8aa0..7e3b362 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
@@ -21,7 +21,6 @@
import android.content.res.ColorStateList
import android.graphics.Color.TRANSPARENT
import android.util.AttributeSet
-import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
@@ -57,7 +56,7 @@
}
init {
- LayoutInflater.from(context).inflate(R.layout.taskbar_all_apps_button, null, false)
+ contentDescription = context.getString(R.string.all_apps_button_label)
setUpIcon()
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
index 1fb835a..344f163 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
@@ -21,7 +21,6 @@
import android.content.res.ColorStateList
import android.graphics.Color.TRANSPARENT
import android.util.AttributeSet
-import android.view.LayoutInflater
import androidx.core.view.setPadding
import com.android.launcher3.R
import com.android.launcher3.Utilities.dpToPx
@@ -33,11 +32,8 @@
/** Taskbar divider view container for customizable taskbar. */
class TaskbarDividerContainer
@JvmOverloads
-constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0,
-) : IconButtonView(context, attrs), TaskbarContainer {
+constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
+ IconButtonView(context, attrs), TaskbarContainer {
private val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context)
override val spaceNeeded: Int
@@ -46,7 +42,7 @@
}
init {
- LayoutInflater.from(context).inflate(R.layout.taskbar_divider, null, false)
+ contentDescription = context.getString(R.string.taskbar_divider_a11y_title)
setUpIcon()
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index fe68ebc..34d9a68 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -190,6 +190,7 @@
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.RecentsViewContainer;
import com.android.quickstep.views.TaskView;
+import com.android.systemui.animation.back.FlingOnBackAnimationCallback;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.unfold.RemoteUnfoldSharedComponent;
@@ -900,12 +901,12 @@
protected void registerBackDispatcher() {
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
- new OnBackAnimationCallback() {
+ new FlingOnBackAnimationCallback() {
@Nullable OnBackAnimationCallback mActiveOnBackAnimationCallback;
@Override
- public void onBackStarted(@NonNull BackEvent backEvent) {
+ public void onBackStartedCompat(@NonNull BackEvent backEvent) {
if (mActiveOnBackAnimationCallback != null) {
mActiveOnBackAnimationCallback.onBackCancelled();
}
@@ -915,7 +916,7 @@
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Override
- public void onBackInvoked() {
+ public void onBackInvokedCompat() {
// Recreate mActiveOnBackAnimationCallback if necessary to avoid NPE
// because:
// 1. b/260636433: In 3-button-navigation mode, onBackStarted() is not
@@ -931,7 +932,7 @@
}
@Override
- public void onBackProgressed(@NonNull BackEvent backEvent) {
+ public void onBackProgressedCompat(@NonNull BackEvent backEvent) {
if (!FeatureFlags.IS_STUDIO_BUILD
&& mActiveOnBackAnimationCallback == null) {
return;
@@ -940,7 +941,7 @@
}
@Override
- public void onBackCancelled() {
+ public void onBackCancelledCompat() {
if (!FeatureFlags.IS_STUDIO_BUILD
&& mActiveOnBackAnimationCallback == null) {
return;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 3a39cf2..8ad00bf 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -95,6 +95,7 @@
public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
StateAnimationConfig config) {
RecentsView overview = mContainer.getOverviewPanel();
+ boolean isPinnedTaskbar = DisplayController.isPinnedTaskbar(mContainer);
if ((fromState == OVERVIEW || fromState == OVERVIEW_SPLIT_SELECT) && toState == NORMAL) {
overview.switchToScreenshot(() ->
overview.finishRecentsAnimation(true /* toRecents */, null));
@@ -109,7 +110,8 @@
// We sync the scrim fade with the taskbar animation duration to avoid any flickers for
// taskbar icons disappearing before hotseat icons show up.
float scrimUpperBoundFromSplit =
- QuickstepTransitionManager.getTaskbarToHomeDuration() / (float) config.duration;
+ QuickstepTransitionManager.getTaskbarToHomeDuration(isPinnedTaskbar)
+ / (float) config.duration;
scrimUpperBoundFromSplit = Math.min(scrimUpperBoundFromSplit, 1f);
config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(LINEAR, 0, 0.25f));
config.setInterpolator(ANIM_SCRIM_FADE,
@@ -139,7 +141,8 @@
// Sync scroll so that it ends before or at the same time as the taskbar animation.
if (mContainer.getDeviceProfile().isTaskbarPresent) {
config.duration = Math.min(
- config.duration, QuickstepTransitionManager.getTaskbarToHomeDuration());
+ config.duration,
+ QuickstepTransitionManager.getTaskbarToHomeDuration(isPinnedTaskbar));
}
overview.snapToPage(DEFAULT_PAGE, Math.toIntExact(config.duration));
} else {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 97d7179..95e7737 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -100,6 +100,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
@@ -667,7 +668,7 @@
TopTaskTracker.CachedTaskInfo cachedTaskInfo = mGestureState.getRunningTask();
if (mIsSwipeForSplit) {
int[] splitTaskIds = TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds();
- runningTasks = cachedTaskInfo.getPlaceholderTasks(splitTaskIds);
+ runningTasks = cachedTaskInfo.getSplitPlaceholderTasks(splitTaskIds);
} else {
runningTasks = cachedTaskInfo.getPlaceholderTasks();
}
@@ -1373,8 +1374,9 @@
mInputConsumerProxy.enable();
}
if (endTarget == HOME) {
+ boolean isPinnedTaskbar = DisplayController.isPinnedTaskbar(mContext);
duration = mContainer != null && mContainer.getDeviceProfile().isTaskbarPresent
- ? StaggeredWorkspaceAnim.DURATION_TASKBAR_MS
+ ? QuickstepTransitionManager.getTaskbarToHomeDuration(isPinnedTaskbar)
: StaggeredWorkspaceAnim.DURATION_MS;
ContextualEduStatsManager.INSTANCE.get(mContext).updateEduStats(
mGestureState.isTrackpadGesture(), GestureType.HOME);
diff --git a/quickstep/src/com/android/quickstep/BaseContainerInterface.java b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
index a3953ca..2164bc2 100644
--- a/quickstep/src/com/android/quickstep/BaseContainerInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
@@ -378,6 +378,9 @@
public static void getTaskDimension(Context context, DeviceProfile dp, PointF out) {
out.x = dp.widthPx;
out.y = dp.heightPx;
+ if (dp.isTablet && !DisplayController.isTransientTaskbar(context)) {
+ out.y -= dp.taskbarHeight;
+ }
}
/**
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index cff352c..5190ec8 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -28,6 +28,7 @@
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_HOME;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_NEW_TASK;
+import android.app.TaskInfo;
import android.content.Intent;
import android.os.SystemClock;
import android.view.MotionEvent;
@@ -45,6 +46,7 @@
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.views.RecentsViewContainer;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.wm.shell.shared.GroupedTaskInfo;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -330,13 +332,23 @@
if (mRunningTask == null) {
return new int[]{INVALID_TASK_ID, INVALID_TASK_ID};
} else {
- int cachedTasksSize = mRunningTask.mAllCachedTasks.size();
- int count = Math.min(cachedTasksSize, getMultipleTasks ? 2 : 1);
- int[] runningTaskIds = new int[count];
- for (int i = 0; i < count; i++) {
- runningTaskIds[i] = mRunningTask.mAllCachedTasks.get(i).taskId;
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ if (mRunningTask.getVisibleTasks().isEmpty()) {
+ return new int[0];
+ }
+ GroupedTaskInfo topRunningTask = mRunningTask.getVisibleTasks().getFirst();
+ List<TaskInfo> groupedTasks = topRunningTask.getTaskInfoList();
+ return groupedTasks.stream().mapToInt(
+ groupedTask -> groupedTask.taskId).toArray();
+ } else {
+ int cachedTasksSize = mRunningTask.mAllCachedTasks.size();
+ int count = Math.min(cachedTasksSize, getMultipleTasks ? 2 : 1);
+ int[] runningTaskIds = new int[count];
+ for (int i = 0; i < count; i++) {
+ runningTaskIds[i] = mRunningTask.mAllCachedTasks.get(i).taskId;
+ }
+ return runningTaskIds;
}
- return runningTaskIds;
}
}
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 1124aac..6719ab7 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -24,6 +24,7 @@
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.window.flags.Flags.predictiveBackThreeButtonNav;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -60,6 +61,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.quickstep.util.BackAnimState;
import com.android.systemui.shared.system.QuickStepContract;
@@ -295,8 +298,11 @@
mStartRect.set(appTarget.windowConfiguration.getMaxBounds());
- // inset bottom in case of pinned taskbar being present
- mStartRect.inset(0, 0, 0, appTarget.contentInsets.bottom);
+ // inset bottom in case of taskbar being present
+ if (!predictiveBackThreeButtonNav() || mLauncher.getDeviceProfile().isTaskbarPresent
+ || DisplayController.getNavigationMode(mLauncher) == NavigationMode.NO_BUTTON) {
+ mStartRect.inset(0, 0, 0, appTarget.contentInsets.bottom);
+ }
mLauncherTargetView = mQuickstepTransitionManager.findLauncherView(
new RemoteAnimationTarget[]{ mBackTarget });
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 714838a..85e2b6e 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -114,12 +114,9 @@
}
@Override
- public void onTaskMovedToFront(GroupedTaskInfo[] visibleTasks) {
+ public void onTaskMovedToFront(GroupedTaskInfo taskToFront) {
mMainThreadExecutor.execute(() -> {
- // TODO(b/346588978): We currently are only sending a single task, but this will
- // be updated once we send the full set of visible tasks
- final TaskInfo info = visibleTasks[0].getTaskInfo1();
- topTaskTracker.handleTaskMovedToFront(info);
+ topTaskTracker.handleTaskMovedToFront(taskToFront.getTaskInfo1());
});
}
@@ -127,6 +124,13 @@
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
mMainThreadExecutor.execute(() -> topTaskTracker.onTaskChanged(taskInfo));
}
+
+ @Override
+ public void onVisibleTasksChanged(GroupedTaskInfo[] visibleTasks) {
+ mMainThreadExecutor.execute(() -> {
+ topTaskTracker.onVisibleTasksChanged(visibleTasks);
+ });
+ }
});
// We may receive onRunningTaskAppeared events later for tasks which have already been
// included in the list returned by mSysUiProxy.getRunningTasks(), or may receive
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index dcb0108..60fcff8 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -53,6 +53,8 @@
private boolean mFinishRequested = false;
// Only valid when mFinishRequested == true.
private boolean mFinishTargetIsLauncher;
+ // Only valid when mFinishRequested == true
+ private boolean mLauncherIsVisibleAtFinish;
private RunnableList mPendingFinishCallbacks = new RunnableList();
public RecentsAnimationController(RecentsAnimationControllerCompat controller,
@@ -117,13 +119,27 @@
}
@UiThread
+ public void finish(boolean toRecents, boolean launcherIsVisibleAtFinish,
+ Runnable onFinishComplete, boolean sendUserLeaveHint) {
+ Preconditions.assertUIThread();
+ finishController(toRecents, launcherIsVisibleAtFinish, onFinishComplete, sendUserLeaveHint,
+ false);
+ }
+
+ @UiThread
public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
- finishController(toRecents, callback, sendUserLeaveHint, false /* forceFinish */);
+ finishController(toRecents, false, callback, sendUserLeaveHint, false /* forceFinish */);
}
@UiThread
public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint,
boolean forceFinish) {
+ finishController(toRecents, toRecents, callback, sendUserLeaveHint, forceFinish);
+ }
+
+ @UiThread
+ public void finishController(boolean toRecents, boolean launcherIsVisibleAtFinish,
+ Runnable callback, boolean sendUserLeaveHint, boolean forceFinish) {
mPendingFinishCallbacks.add(callback);
if (!forceFinish && mFinishRequested) {
// If finish has already been requested, then add the callback to the pending list.
@@ -135,6 +151,7 @@
// Finish not yet requested
mFinishRequested = true;
mFinishTargetIsLauncher = toRecents;
+ mLauncherIsVisibleAtFinish = launcherIsVisibleAtFinish;
mOnFinishedListener.accept(this);
Runnable finishCb = () -> {
mController.finish(toRecents, sendUserLeaveHint, new IResultReceiver.Stub() {
@@ -211,6 +228,14 @@
return mFinishTargetIsLauncher;
}
+ /**
+ * RecentsAnimationListeners can check this in onRecentsAnimationFinished() to determine whether
+ * the animation was finished to launcher vs an app.
+ */
+ public boolean getLauncherIsVisibleAtFinish() {
+ return mLauncherIsVisibleAtFinish;
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "RecentsAnimationController:");
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index de8be50..e296449 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -354,7 +354,11 @@
* @return whether the given running task info matches the gesture-blocked task.
*/
public boolean isGestureBlockedTask(CachedTaskInfo taskInfo) {
- return taskInfo != null && taskInfo.getTaskId() == mGestureBlockingTaskId;
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ return taskInfo != null && taskInfo.topGroupedTaskContainsTask(mGestureBlockingTaskId);
+ } else {
+ return taskInfo != null && taskInfo.getTaskId() == mGestureBlockingTaskId;
+ }
}
/**
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index c618422..6c4c74c 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -886,10 +886,12 @@
/**
* Tells SysUI to update the bubble bar location to the new location.
* @param location new location for the bubble bar
+ * @param source what triggered the location update
*/
- public void setBubbleBarLocation(BubbleBarLocation location) {
+ public void setBubbleBarLocation(BubbleBarLocation location,
+ @BubbleBarLocation.UpdateSource int source) {
try {
- mBubbles.setBubbleBarLocation(location);
+ mBubbles.setBubbleBarLocation(location, source);
} catch (RemoteException e) {
Log.w(TAG, "Failed call setBubbleBarLocation");
}
diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java
index c9dfe6d..7065f37 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -18,17 +18,20 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.content.Intent.ACTION_CHOOSER;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_A;
+import static com.android.wm.shell.Flags.enableFlexibleSplit;
+import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT;
-import android.annotation.UserIdInt;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
import android.content.Context;
+import android.util.ArrayMap;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -46,8 +49,10 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -61,37 +66,58 @@
*/
public class TopTaskTracker extends ISplitScreenListener.Stub
implements TaskStackChangeListener, SafeCloseable {
-
+ private static final String TAG = "TopTaskTracker";
public static MainThreadInitializedObject<TopTaskTracker> INSTANCE =
new MainThreadInitializedObject<>(TopTaskTracker::new);
private static final int HISTORY_SIZE = 5;
+ private final Context mContext;
+
+ // Only used when Flags.enableShellTopTaskTracking() is disabled
// Ordered list with first item being the most recent task.
private final LinkedList<TaskInfo> mOrderedTaskList = new LinkedList<>();
-
- private final Context mContext;
private final SplitStageInfo mMainStagePosition = new SplitStageInfo();
private final SplitStageInfo mSideStagePosition = new SplitStageInfo();
private int mPinnedTaskId = INVALID_TASK_ID;
+ // Only used when Flags.enableShellTopTaskTracking() is enabled
+ // Mapping of display id to running tasks. Running tasks are ordered from top most to
+ // bottom most.
+ private ArrayMap<Integer, ArrayList<GroupedTaskInfo>> mVisibleTasks = new ArrayMap<>();
+
private TopTaskTracker(Context context) {
mContext = context;
- mMainStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_MAIN;
- mSideStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_SIDE;
- TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
- SystemUiProxy.INSTANCE.get(context).registerSplitScreenListener(this);
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ // Just prepopulate a list for the default display tasks so we don't need to add null
+ // checks everywhere
+ mVisibleTasks.put(DEFAULT_DISPLAY, new ArrayList<>());
+ } else {
+ mMainStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_MAIN;
+ mSideStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_SIDE;
+
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
+ SystemUiProxy.INSTANCE.get(context).registerSplitScreenListener(this);
+ }
}
@Override
public void close() {
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ return;
+ }
+
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(this);
SystemUiProxy.INSTANCE.get(mContext).unregisterSplitScreenListener(this);
}
@Override
public void onTaskRemoved(int taskId) {
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ return;
+ }
+
mOrderedTaskList.removeIf(rto -> rto.taskId == taskId);
}
@@ -100,7 +126,11 @@
handleTaskMovedToFront(taskInfo);
}
- public void handleTaskMovedToFront(TaskInfo taskInfo) {
+ void handleTaskMovedToFront(TaskInfo taskInfo) {
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ return;
+ }
+
mOrderedTaskList.removeIf(rto -> rto.taskId == taskInfo.taskId);
mOrderedTaskList.addFirst(taskInfo);
@@ -131,8 +161,39 @@
}
}
+ /**
+ * Called when the set of visible tasks have changed.
+ */
+ public void onVisibleTasksChanged(GroupedTaskInfo[] visibleTasks) {
+ if (!com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ return;
+ }
+
+ // TODO(346588978): Per-display info, just have everything in order by display
+
+ // Clear existing tasks for each display
+ mVisibleTasks.forEach((displayId, visibleTasksOnDisplay) -> visibleTasksOnDisplay.clear());
+
+ // Update the visible tasks on each display
+ for (int i = 0; i < visibleTasks.length; i++) {
+ final int displayId = visibleTasks[i].getTaskInfo1().getDisplayId();
+ final ArrayList<GroupedTaskInfo> displayTasks;
+ if (mVisibleTasks.containsKey(displayId)) {
+ displayTasks = mVisibleTasks.get(displayId);
+ } else {
+ displayTasks = new ArrayList<>();
+ mVisibleTasks.put(displayId, displayTasks);
+ }
+ displayTasks.add(visibleTasks[i]);
+ }
+ }
+
@Override
public void onStagePositionChanged(@StageType int stage, @StagePosition int position) {
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ return;
+ }
+
if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) {
mMainStagePosition.stagePosition = position;
} else {
@@ -141,6 +202,10 @@
}
public void onTaskChanged(RunningTaskInfo taskInfo) {
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ return;
+ }
+
for (int i = 0; i < mOrderedTaskList.size(); i++) {
if (mOrderedTaskList.get(i).taskId == taskInfo.taskId) {
mOrderedTaskList.set(i, taskInfo);
@@ -151,6 +216,10 @@
@Override
public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ return;
+ }
+
// If a task is not visible anymore or has been moved to undefined, stop tracking it.
if (!visible || stage == SplitConfigurationOptions.STAGE_TYPE_UNDEFINED) {
if (mMainStagePosition.taskId == taskId) {
@@ -161,7 +230,8 @@
return;
}
- if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) {
+ if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN
+ || (enableFlexibleSplit() && stage == STAGE_TYPE_A)) {
mMainStagePosition.taskId = taskId;
} else {
mSideStagePosition.taskId = taskId;
@@ -170,11 +240,19 @@
@Override
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ return;
+ }
+
mPinnedTaskId = taskId;
}
@Override
public void onActivityUnpinned() {
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ return;
+ }
+
mPinnedTaskId = INVALID_TASK_ID;
}
@@ -183,21 +261,59 @@
* Will return empty array if device is not in staged split
*/
public int[] getRunningSplitTaskIds() {
- if (mMainStagePosition.taskId == INVALID_TASK_ID
- || mSideStagePosition.taskId == INVALID_TASK_ID) {
- return new int[]{};
- }
- int[] out = new int[2];
- if (mMainStagePosition.stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
- out[0] = mMainStagePosition.taskId;
- out[1] = mSideStagePosition.taskId;
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ // TODO(346588978): This assumes default display for now
+ final ArrayList<GroupedTaskInfo> visibleTasks = mVisibleTasks.get(DEFAULT_DISPLAY);
+ final GroupedTaskInfo splitTaskInfo = visibleTasks.stream()
+ .filter(taskInfo -> taskInfo.getType() == TYPE_SPLIT)
+ .findFirst().orElse(null);
+ if (splitTaskInfo != null && splitTaskInfo.getSplitBounds() != null) {
+ return new int[] {
+ splitTaskInfo.getSplitBounds().leftTopTaskId,
+ splitTaskInfo.getSplitBounds().rightBottomTaskId
+ };
+ }
+ return new int[0];
} else {
- out[1] = mMainStagePosition.taskId;
- out[0] = mSideStagePosition.taskId;
+ if (mMainStagePosition.taskId == INVALID_TASK_ID
+ || mSideStagePosition.taskId == INVALID_TASK_ID) {
+ return new int[]{};
+ }
+ int[] out = new int[2];
+ if (mMainStagePosition.stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+ out[0] = mMainStagePosition.taskId;
+ out[1] = mSideStagePosition.taskId;
+ } else {
+ out[1] = mMainStagePosition.taskId;
+ out[0] = mSideStagePosition.taskId;
+ }
+ return out;
}
- return out;
}
+ /**
+ * Dumps the list of tasks in top task tracker.
+ */
+ public void dump(PrintWriter pw) {
+ if (!com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ return;
+ }
+
+ // TODO(346588978): This assumes default display for now
+ final ArrayList<GroupedTaskInfo> displayTasks = mVisibleTasks.get(DEFAULT_DISPLAY);
+ pw.println("TopTaskTracker:");
+ pw.println(" tasks: [");
+ for (GroupedTaskInfo taskInfo : displayTasks) {
+ final TaskInfo info = taskInfo.getTaskInfo1();
+ final boolean isExcluded = (info.baseIntent.getFlags()
+ & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
+ pw.println(" " + info.taskId + ": excluded=" + isExcluded
+ + " visibleRequested=" + info.isVisibleRequested
+ + " visible=" + info.isVisible
+ + " " + info.baseIntent.getComponent());
+ }
+ pw.println(" ]");
+ }
/**
* Returns the CachedTaskInfo for the top most task
@@ -205,25 +321,35 @@
@NonNull
@UiThread
public CachedTaskInfo getCachedTopTask(boolean filterOnlyVisibleRecents) {
- if (filterOnlyVisibleRecents) {
- // Since we only know about the top most task, any filtering may not be applied on the
- // cache. The second to top task may change while the top task is still the same.
- RunningTaskInfo[] tasks = TraceHelper.allowIpcs("getCachedTopTask.true", () ->
- ActivityManagerWrapper.getInstance().getRunningTasks(true));
- return new CachedTaskInfo(Arrays.asList(tasks));
- }
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ // TODO(346588978): Currently ignore filterOnlyVisibleRecents, but perhaps make this an
+ // explicit filter For things to ignore (ie. PIP/Bubbles/Assistant/etc/so that this is
+ // explicit)
+ // TODO(346588978): This assumes default display for now (as does all of Launcher)
+ final ArrayList<GroupedTaskInfo> displayTasks = mVisibleTasks.get(DEFAULT_DISPLAY);
+ return new CachedTaskInfo(new ArrayList<>(displayTasks));
+ } else {
+ if (filterOnlyVisibleRecents) {
+ // Since we only know about the top most task, any filtering may not be applied on
+ // the cache. The second to top task may change while the top task is still the
+ // same.
+ RunningTaskInfo[] tasks = TraceHelper.allowIpcs("getCachedTopTask.true", () ->
+ ActivityManagerWrapper.getInstance().getRunningTasks(true));
+ return new CachedTaskInfo(Arrays.asList(tasks));
+ }
- if (mOrderedTaskList.isEmpty()) {
- RunningTaskInfo[] tasks = TraceHelper.allowIpcs("getCachedTopTask.false", () ->
- ActivityManagerWrapper.getInstance().getRunningTasks(
- false /* filterOnlyVisibleRecents */));
- Collections.addAll(mOrderedTaskList, tasks);
- }
+ if (mOrderedTaskList.isEmpty()) {
+ RunningTaskInfo[] tasks = TraceHelper.allowIpcs("getCachedTopTask.false", () ->
+ ActivityManagerWrapper.getInstance().getRunningTasks(
+ false /* filterOnlyVisibleRecents */));
+ Collections.addAll(mOrderedTaskList, tasks);
+ }
- ArrayList<TaskInfo> tasks = new ArrayList<>(mOrderedTaskList);
- // Strip the pinned task and recents task
- tasks.removeIf(t -> t.taskId == mPinnedTaskId || isRecentsTask(t));
- return new CachedTaskInfo(tasks);
+ ArrayList<TaskInfo> tasks = new ArrayList<>(mOrderedTaskList);
+ // Strip the pinned task and recents task
+ tasks.removeIf(t -> t.taskId == mPinnedTaskId || isRecentsTask(t));
+ return new CachedTaskInfo(tasks);
+ }
}
private static boolean isRecentsTask(TaskInfo task) {
@@ -237,24 +363,79 @@
*/
public static class CachedTaskInfo {
+ // Only used when enableShellTopTaskTracking() is disabled
@Nullable
private final TaskInfo mTopTask;
+ @Nullable
public final List<TaskInfo> mAllCachedTasks;
- CachedTaskInfo(List<TaskInfo> allCachedTasks) {
+ // Only used when enableShellTopTaskTracking() is enabled
+ @Nullable
+ private final GroupedTaskInfo mTopGroupedTask;
+ @Nullable
+ private final ArrayList<GroupedTaskInfo> mVisibleTasks;
+
+
+ // Only used when enableShellTopTaskTracking() is enabled
+ CachedTaskInfo(@NonNull ArrayList<GroupedTaskInfo> visibleTasks) {
+ mAllCachedTasks = null;
+ mTopTask = null;
+ mVisibleTasks = visibleTasks;
+ mTopGroupedTask = !mVisibleTasks.isEmpty() ? mVisibleTasks.getFirst() : null;
+
+ }
+
+ // Only used when enableShellTopTaskTracking() is disabled
+ CachedTaskInfo(@NonNull List<TaskInfo> allCachedTasks) {
+ mVisibleTasks = null;
+ mTopGroupedTask = null;
mAllCachedTasks = allCachedTasks;
mTopTask = allCachedTasks.isEmpty() ? null : allCachedTasks.get(0);
}
+ /**
+ * @return The list of visible tasks
+ */
+ public ArrayList<GroupedTaskInfo> getVisibleTasks() {
+ return mVisibleTasks;
+ }
+
+ /**
+ * @return The top task id
+ */
public int getTaskId() {
- return mTopTask == null ? INVALID_TASK_ID : mTopTask.taskId;
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ // Callers should use topGroupedTaskContainsTask() instead
+ return INVALID_TASK_ID;
+ } else {
+ return mTopTask != null ? mTopTask.taskId : INVALID_TASK_ID;
+ }
+ }
+
+ /**
+ * @return Whether the top grouped task contains the given {@param taskId} if
+ * Flags.enableShellTopTaskTracking() is true, otherwise it checks the top
+ * task as reported from TaskStackListener.
+ */
+ public boolean topGroupedTaskContainsTask(int taskId) {
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ return mTopGroupedTask != null && mTopGroupedTask.containsTask(taskId);
+ } else {
+ return mTopTask != null && mTopTask.taskId == taskId;
+ }
}
/**
* Returns true if the root of the task chooser activity
*/
public boolean isRootChooseActivity() {
- return mTopTask != null && ACTION_CHOOSER.equals(mTopTask.baseIntent.getAction());
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ // TODO(346588978): Update this to not make an assumption on a specific task info
+ return mTopGroupedTask != null && ACTION_CHOOSER.equals(
+ mTopGroupedTask.getTaskInfo1().baseIntent.getAction());
+ } else {
+ return mTopTask != null && ACTION_CHOOSER.equals(mTopTask.baseIntent.getAction());
+ }
}
/**
@@ -262,6 +443,10 @@
* is another running task that is not excluded from recents, returns that underlying task.
*/
public @Nullable CachedTaskInfo getVisibleNonExcludedTask() {
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ // Callers should not need this when the full set of visible tasks are provided
+ return null;
+ }
if (mTopTask == null
|| (mTopTask.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0) {
// Not an excluded task.
@@ -278,24 +463,30 @@
}
/**
- * Returns true if this represents the HOME task
+ * Returns true if this represents the HOME activity type task
*/
public boolean isHomeTask() {
- return mTopTask != null && mTopTask.configuration.windowConfiguration
- .getActivityType() == ACTIVITY_TYPE_HOME;
- }
-
- public boolean isRecentsTask() {
- return TopTaskTracker.isRecentsTask(mTopTask);
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ // TODO(346588978): Update this to not make an assumption on a specific task info
+ return mTopGroupedTask != null
+ && mTopGroupedTask.getTaskInfo1().getActivityType() == ACTIVITY_TYPE_HOME;
+ } else {
+ return mTopTask != null && mTopTask.configuration.windowConfiguration
+ .getActivityType() == ACTIVITY_TYPE_HOME;
+ }
}
/**
- * Returns {@code true} if this task windowing mode is set to {@link
- * android.app.WindowConfiguration#WINDOWING_MODE_FREEFORM}
+ * Returns true if this represents the RECENTS activity type task
*/
- public boolean isFreeformTask() {
- return mTopTask != null && mTopTask.configuration.windowConfiguration.getWindowingMode()
- == WINDOWING_MODE_FREEFORM;
+ public boolean isRecentsTask() {
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ // TODO(346588978): Update this to not make an assumption on a specific task info
+ return mTopGroupedTask != null
+ && TopTaskTracker.isRecentsTask(mTopGroupedTask.getTaskInfo1());
+ } else {
+ return TopTaskTracker.isRecentsTask(mTopTask);
+ }
}
/**
@@ -303,43 +494,78 @@
* is loaded by the model
*/
public Task[] getPlaceholderTasks() {
- return mTopTask == null ? new Task[0]
- : new Task[]{Task.from(new TaskKey(mTopTask), mTopTask, false)};
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ // TODO(346588978): Update this to return more than a single task once the callers
+ // are refactored
+ if (mVisibleTasks.isEmpty()) {
+ return new Task[0];
+ }
+ final TaskInfo info = mVisibleTasks.getFirst().getTaskInfo1();
+ return new Task[]{Task.from(new TaskKey(info), info, false)};
+ } else {
+ return mTopTask == null ? new Task[0]
+ : new Task[]{Task.from(new TaskKey(mTopTask), mTopTask, false)};
+ }
}
/**
* Returns {@link Task} array corresponding to the provided task ids which can be used as a
* placeholder until the true object is loaded by the model
*/
- public Task[] getPlaceholderTasks(int[] taskIds) {
- if (mTopTask == null) {
- return new Task[0];
- }
- Task[] result = new Task[taskIds.length];
- for (int i = 0; i < taskIds.length; i++) {
- final int index = i;
- int taskId = taskIds[i];
- mAllCachedTasks.forEach(rti -> {
- if (rti.taskId == taskId) {
- result[index] = Task.from(new TaskKey(rti), rti, false);
- }
- });
- }
- return result;
- }
+ public Task[] getSplitPlaceholderTasks(int[] taskIds) {
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ if (mVisibleTasks.isEmpty()
+ || mVisibleTasks.getFirst().getType() != TYPE_SPLIT) {
+ return new Task[0];
+ }
- @UserIdInt
- @Nullable
- public Integer getUserId() {
- return mTopTask == null ? null : mTopTask.userId;
+ GroupedTaskInfo splitTask = mVisibleTasks.getFirst();
+ Task[] result = new Task[taskIds.length];
+ for (int i = 0; i < taskIds.length; i++) {
+ TaskInfo info = splitTask.getTaskById(taskIds[i]);
+ if (info == null) {
+ Log.w(TAG, "Requested task (" + taskIds[i] + ") not found");
+ return new Task[0];
+ }
+ result[i] = Task.from(new TaskKey(info), info, false);
+ }
+ return result;
+ } else {
+ if (mTopTask == null) {
+ return new Task[0];
+ }
+ Task[] result = new Task[taskIds.length];
+ for (int i = 0; i < taskIds.length; i++) {
+ final int index = i;
+ int taskId = taskIds[i];
+ mAllCachedTasks.forEach(rti -> {
+ if (rti.taskId == taskId) {
+ result[index] = Task.from(new TaskKey(rti), rti, false);
+ }
+ });
+ }
+ return result;
+ }
}
@Nullable
public String getPackageName() {
- if (mTopTask == null || mTopTask.baseActivity == null) {
- return null;
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ // TODO(346588978): Update this to not make an assumption on a specific task info
+ if (mTopGroupedTask == null) {
+ return null;
+ }
+ final TaskInfo info = mTopGroupedTask.getTaskInfo1();
+ if (info.baseActivity == null) {
+ return null;
+ }
+ return info.baseActivity.getPackageName();
+ } else {
+ if (mTopTask == null || mTopTask.baseActivity == null) {
+ return null;
+ }
+ return mTopTask.baseActivity.getPackageName();
}
- return mTopTask.baseActivity.getPackageName();
}
}
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 5b085d2..0242fb6 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -74,6 +74,7 @@
import android.view.View;
import androidx.annotation.BinderThread;
+import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -84,6 +85,7 @@
import com.android.launcher3.Flags;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.desktop.DesktopAppLaunchTransitionManager;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StatefulActivity;
@@ -550,11 +552,15 @@
@Override
public void onInputDeviceAdded(int deviceId) {
if (isTrackpadDevice(deviceId)) {
- boolean wasEmpty = mTrackpadsConnected.isEmpty();
- mTrackpadsConnected.add(deviceId);
- if (wasEmpty) {
- update();
- }
+ // This updates internal TIS state so it needs to also run on the main
+ // thread.
+ MAIN_EXECUTOR.execute(() -> {
+ boolean wasEmpty = mTrackpadsConnected.isEmpty();
+ mTrackpadsConnected.add(deviceId);
+ if (wasEmpty) {
+ update();
+ }
+ });
}
}
@@ -564,12 +570,17 @@
@Override
public void onInputDeviceRemoved(int deviceId) {
- mTrackpadsConnected.remove(deviceId);
- if (mTrackpadsConnected.isEmpty()) {
- update();
- }
+ // This updates internal TIS state so it needs to also run on the main
+ // thread.
+ MAIN_EXECUTOR.execute(() -> {
+ mTrackpadsConnected.remove(deviceId);
+ if (mTrackpadsConnected.isEmpty()) {
+ update();
+ }
+ });
}
+ @MainThread
private void update() {
if (mInputMonitorCompat != null && !mTrackpadsConnected.isEmpty()) {
// Don't destroy and reinitialize input monitor due to trackpad
@@ -580,6 +591,7 @@
}
private boolean isTrackpadDevice(int deviceId) {
+ // This is a blocking binder call that should run on a bg thread.
InputDevice inputDevice = mInputManager.getInputDevice(deviceId);
if (inputDevice == null) {
return false;
@@ -651,6 +663,7 @@
private NavigationMode mGestureStartNavMode = null;
private DesktopVisibilityController mDesktopVisibilityController;
+ private DesktopAppLaunchTransitionManager mDesktopAppLaunchTransitionManager;
@Override
public void onCreate() {
@@ -675,6 +688,9 @@
mDesktopVisibilityController = new DesktopVisibilityController(this);
mTaskbarManager = new TaskbarManager(
this, mAllAppsActionManager, mNavCallbacks, mDesktopVisibilityController);
+ mDesktopAppLaunchTransitionManager =
+ new DesktopAppLaunchTransitionManager(this, SystemUiProxy.INSTANCE.get(this));
+ mDesktopAppLaunchTransitionManager.registerTransitions();
if (Flags.enableLauncherOverviewInWindow() || Flags.enableFallbackOverviewInWindow()) {
mRecentsWindowManager = new RecentsWindowManager(this);
}
@@ -840,6 +856,10 @@
if (mRecentsWindowManager != null) {
mRecentsWindowManager.destroy();
}
+ if (mDesktopAppLaunchTransitionManager != null) {
+ mDesktopAppLaunchTransitionManager.unregisterTransitions();
+ }
+ mDesktopAppLaunchTransitionManager = null;
mDesktopVisibilityController.onDestroy();
sConnected = false;
@@ -1342,15 +1362,17 @@
&& runningTask != null
&& runningTask.isRootChooseActivity();
- // In the case where we are in an excluded, translucent overlay, ignore it and treat the
- // running activity as the task behind the overlay.
- TopTaskTracker.CachedTaskInfo otherVisibleTask = runningTask == null
- ? null
- : runningTask.getVisibleNonExcludedTask();
- if (otherVisibleTask != null) {
- ActiveGestureProtoLogProxy.logUpdateGestureStateRunningTask(
- otherVisibleTask.getPackageName(), runningTask.getPackageName());
- gestureState.updateRunningTask(otherVisibleTask);
+ if (!com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ // In the case where we are in an excluded, translucent overlay, ignore it and treat the
+ // running activity as the task behind the overlay.
+ TopTaskTracker.CachedTaskInfo otherVisibleTask = runningTask == null
+ ? null
+ : runningTask.getVisibleNonExcludedTask();
+ if (otherVisibleTask != null) {
+ ActiveGestureProtoLogProxy.logUpdateGestureStateRunningTask(
+ otherVisibleTask.getPackageName(), runningTask.getPackageName());
+ gestureState.updateRunningTask(otherVisibleTask);
+ }
}
boolean previousGestureAnimatedToLauncher =
@@ -1652,6 +1674,7 @@
ContextualSearchStateManager.INSTANCE.get(this).dump("\t", pw);
SystemUiProxy.INSTANCE.get(this).dump(pw);
DeviceConfigWrapper.get().dump(" ", pw);
+ TopTaskTracker.INSTANCE.get(this).dump(pw);
}
private AbsSwipeUpHandler createLauncherSwipeHandler(
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
index 4f9d837..c1d3f6e 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
@@ -149,7 +149,8 @@
if (mActiveAnimationFactory != null) {
return;
}
- setHomeScaleAndAlpha(builder, app, mCurrentShift.value, 0);
+ setHomeScaleAndAlpha(builder, app, mCurrentShift.value,
+ Utilities.boundToRange(1 - mCurrentShift.value, 0, 1));
}
private void setHomeScaleAndAlpha(SurfaceProperties builder,
diff --git a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
index 275af00..6c627ef 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
@@ -73,26 +73,29 @@
getTaskDataById(taskId).map { it?.thumbnail }.distinctUntilChangedBy { it?.snapshotId }
override fun setVisibleTasks(visibleTaskIdList: Set<Int>) {
- // Remove tasks are no longer visible
val tasksNoLongerVisible = taskRequests.keys.subtract(visibleTaskIdList)
+ val newlyVisibleTasks = visibleTaskIdList.subtract(taskRequests.keys)
+ if (tasksNoLongerVisible.isNotEmpty() || newlyVisibleTasks.isNotEmpty()) {
+ Log.d(
+ TAG,
+ "setVisibleTasks to: $visibleTaskIdList, " +
+ "removed: $tasksNoLongerVisible, added: $newlyVisibleTasks",
+ )
+ }
+
+ // Remove tasks are no longer visible
removeTasks(tasksNoLongerVisible)
// Add new tasks to be requested
- val newlyVisibleTasks = visibleTaskIdList.subtract(taskRequests.keys)
newlyVisibleTasks.forEach { taskId -> requestTaskData(taskId) }
-
- if (tasksNoLongerVisible.isNotEmpty() || newlyVisibleTasks.isNotEmpty()) {
- Log.d(TAG, "setVisibleTasks to: $visibleTaskIdList, " +
- "removed: $tasksNoLongerVisible, added: $newlyVisibleTasks")
- }
}
private fun requestTaskData(taskId: Int) {
- Log.i(TAG, "requestTaskData: $taskId")
val task = tasks.value[taskId] ?: return
taskRequests[taskId] =
Pair(
task.key,
recentsCoroutineScope.launch {
+ Log.i(TAG, "requestTaskData: $taskId")
fetchIcon(task)
fetchThumbnail(task)
},
@@ -102,8 +105,8 @@
private fun removeTasks(tasksToRemove: Set<Int>) {
if (tasksToRemove.isEmpty()) return
+ Log.i(TAG, "removeTasks: $tasksToRemove")
tasksToRemove.forEach { taskId ->
- Log.i(TAG, "removeTask: $taskId")
val request = taskRequests.remove(taskId) ?: return
val (taskKey, job) = request
job.cancel()
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index f2b9976..dd11d48 100644
--- a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
+++ b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.util.Log
import android.view.View
+import com.android.launcher3.util.coroutines.DispatcherProvider
import com.android.launcher3.util.coroutines.ProductionDispatchers
import com.android.quickstep.RecentsModel
import com.android.quickstep.recents.data.RecentTasksRepository
@@ -63,6 +64,7 @@
val recentsCoroutineScope =
CoroutineScope(SupervisorJob() + Dispatchers.Main + CoroutineName("RecentsView"))
set(CoroutineScope::class.java.simpleName, recentsCoroutineScope)
+ set(DispatcherProvider::class.java.simpleName, ProductionDispatchers)
val recentsModel = RecentsModel.INSTANCE.get(appContext)
val taskVisualsChangedDelegate =
TaskVisualsChangedDelegateImpl(
@@ -196,6 +198,7 @@
recentsViewData = inject(),
taskViewData = inject(scopeId, extras),
taskContainerData = inject(scopeId),
+ dispatcherProvider = inject(),
getThumbnailPositionUseCase = inject(),
tasksRepository = inject(),
splashAlphaUseCase = inject(scopeId),
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index eb9c047..a8c8659 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -33,7 +33,6 @@
import com.android.launcher3.util.ViewPool
import com.android.quickstep.recents.di.RecentsDependencies
import com.android.quickstep.recents.di.get
-import com.android.quickstep.recents.di.inject
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
@@ -43,7 +42,6 @@
import com.android.quickstep.util.TaskCornerRadius
import com.android.quickstep.views.FixedSizeImageView
import com.android.systemui.shared.system.QuickStepContract
-import kotlin.math.abs
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -53,8 +51,7 @@
import kotlinx.coroutines.flow.onEach
class TaskThumbnailView : ConstraintLayout, ViewPool.Reusable {
-
- private val viewData: TaskThumbnailViewData by RecentsDependencies.inject(this)
+ private lateinit var viewData: TaskThumbnailViewData
private lateinit var viewModel: TaskThumbnailViewModel
private lateinit var viewAttachedScope: CoroutineScope
@@ -92,10 +89,12 @@
super.onAttachedToWindow()
viewAttachedScope =
CoroutineScope(SupervisorJob() + Dispatchers.Main + CoroutineName("TaskThumbnailView"))
+ viewData = RecentsDependencies.get(this)
+ updateViewDataValues()
viewModel = RecentsDependencies.get(this)
viewModel.uiState
.onEach { viewModelUiState ->
- Log.d(TAG, "viewModelUiState changed from $uiState to: $viewModelUiState")
+ Log.d(TAG, "viewModelUiState changed from: $uiState to: $viewModelUiState")
uiState = viewModelUiState
resetViews()
when (viewModelUiState) {
@@ -144,11 +143,15 @@
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
if (changed) {
- viewData.width.value = abs(right - left)
- viewData.height.value = abs(bottom - top)
+ updateViewDataValues()
}
}
+ private fun updateViewDataValues() {
+ viewData.width.value = width
+ viewData.height.value = height
+ }
+
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
if (uiState is SnapshotSplash) {
diff --git a/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt b/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
index c82ed9a..203177a 100644
--- a/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
+++ b/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
@@ -85,14 +85,18 @@
}
private fun initOverlay(enabledState: Enabled) {
- Log.d(TAG, "initOverlay - taskId: ${task.key.id}, thumbnail: ${enabledState.thumbnail}")
+ if (DEBUG) {
+ Log.d(TAG, "initOverlay - taskId: ${task.key.id}, thumbnail: ${enabledState.thumbnail}")
+ }
with(getThumbnailPositionState()) {
overlay.initOverlay(task, enabledState.thumbnail, matrix, isRotated)
}
}
private fun reset() {
- Log.d(TAG, "reset - taskId: ${task.key.id}")
+ if (DEBUG) {
+ Log.d(TAG, "reset - taskId: ${task.key.id}")
+ }
overlay.reset()
}
@@ -105,5 +109,6 @@
companion object {
private const val TAG = "TaskOverlayHelper"
+ private const val DEBUG = false
}
}
diff --git a/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModelImpl.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModelImpl.kt
index bd47cec..8b15a82 100644
--- a/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModelImpl.kt
+++ b/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModelImpl.kt
@@ -21,6 +21,7 @@
import android.graphics.Matrix
import android.util.Log
import androidx.core.graphics.ColorUtils
+import com.android.launcher3.util.coroutines.DispatcherProvider
import com.android.quickstep.recents.data.RecentTasksRepository
import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
import com.android.quickstep.recents.usecase.ThumbnailPositionState
@@ -42,6 +43,7 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
@@ -50,6 +52,7 @@
recentsViewData: RecentsViewData,
taskViewData: TaskViewData,
taskContainerData: TaskContainerData,
+ dispatcherProvider: DispatcherProvider,
private val tasksRepository: RecentTasksRepository,
private val getThumbnailPositionUseCase: GetThumbnailPositionUseCase,
private val splashAlphaUseCase: SplashAlphaUseCase,
@@ -73,6 +76,7 @@
tintAmount ->
max(taskMenuOpenProgress * MAX_SCRIM_ALPHA, tintAmount)
}
+
override val splashAlpha = splashProgress.flatMapLatest { it }
private val isLiveTile =
@@ -84,6 +88,7 @@
runningTaskIds.contains(taskId) && !runningTaskShowScreenshot
}
.distinctUntilChanged()
+ .flowOn(dispatcherProvider.default)
override val uiState: Flow<TaskThumbnailUiState> =
combine(task.flatMapLatest { it }, isLiveTile) { taskVal, isRunning ->
@@ -105,6 +110,7 @@
}
}
.distinctUntilChanged()
+ .flowOn(dispatcherProvider.default)
override fun bind(taskId: Int) {
Log.d(TAG, "bind taskId: $taskId")
diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index 7388d59..1312aa4 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -409,8 +409,8 @@
);
} else {
// Tapped an app pair while in a single app
- int runningTaskId = topTaskTracker
- .getCachedTopTask(false /* filterOnlyVisibleRecents */).getTaskId();
+ final TopTaskTracker.CachedTaskInfo runningTask = topTaskTracker
+ .getCachedTopTask(false /* filterOnlyVisibleRecents */);
mSplitSelectStateController.findLastActiveTasksAndRunCallback(
componentKeys,
@@ -418,10 +418,21 @@
foundTasks -> {
Task foundTask1 = foundTasks[0];
Task foundTask2 = foundTasks[1];
- boolean task1IsOnScreen =
- foundTask1 != null && foundTask1.getKey().getId() == runningTaskId;
- boolean task2IsOnScreen =
- foundTask2 != null && foundTask2.getKey().getId() == runningTaskId;
+ boolean task1IsOnScreen;
+ boolean task2IsOnScreen;
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ task1IsOnScreen = foundTask1 != null
+ && runningTask.topGroupedTaskContainsTask(
+ foundTask1.getKey().getId());
+ task2IsOnScreen = foundTask2 != null
+ && runningTask.topGroupedTaskContainsTask(
+ foundTask2.getKey().getId());
+ } else {
+ task1IsOnScreen = foundTask1 != null && foundTask1.getKey().getId()
+ == runningTask.getTaskId();
+ task2IsOnScreen = foundTask2 != null && foundTask2.getKey().getId()
+ == runningTask.getTaskId();
+ }
if (!task1IsOnScreen && !task2IsOnScreen) {
// Neither App A nor App B are on-screen, launch the app pair normally.
diff --git a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
index db02f55..f719bed 100644
--- a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
+++ b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
@@ -217,6 +217,11 @@
animation.addListener(
AnimatorListeners.forEndCallback(
Runnable {
+ // The workspace might stay at a transparent state when the animation is
+ // cancelled, and the alpha will not be recovered (this doesn't apply to scales
+ // somehow). Resetting the alpha for the workspace here.
+ workspace.alpha = 1.0F
+
workspace.setLayerType(View.LAYER_TYPE_NONE, null)
hotseat.setLayerType(View.LAYER_TYPE_NONE, null)
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index ea582c4..d35a36a 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -163,6 +163,8 @@
*/
private Pair<InstanceId, com.android.launcher3.logging.InstanceId> mSessionInstanceIds;
+ private boolean mIsDestroyed = false;
+
private final BackPressHandler mSplitBackHandler = new BackPressHandler() {
@Override
public boolean canHandleBack() {
@@ -199,6 +201,7 @@
public void onDestroy() {
mContainer = null;
+ mIsDestroyed = true;
mActivityBackCallback = null;
mAppPairsController.onDestroy();
mSplitSelectDataHolder.onDestroy();
@@ -744,7 +747,9 @@
*/
public void resetState() {
mSplitSelectDataHolder.resetState();
- mContainer.<RecentsView>getOverviewPanel().resetDesktopTaskFromSplitSelectState();
+ if (!mIsDestroyed) {
+ mContainer.<RecentsView>getOverviewPanel().resetDesktopTaskFromSplitSelectState();
+ }
dispatchOnSplitSelectionExit();
mRecentsAnimationRunning = false;
mLaunchingTaskView = null;
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 997a842..12ca257 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -49,6 +49,7 @@
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DynamicResource;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.ResourceProvider;
@@ -63,8 +64,7 @@
private static final int APP_CLOSE_ROW_START_DELAY_MS = 10;
// Should be used for animations running alongside this StaggeredWorkspaceAnim.
public static final int DURATION_MS = 250;
- public static final int DURATION_TASKBAR_MS =
- QuickstepTransitionManager.getTaskbarToHomeDuration();
+ private final int mTaskbarDurationInMs;
private static final float MAX_VELOCITY_PX_PER_S = 22f;
@@ -81,6 +81,8 @@
public StaggeredWorkspaceAnim(QuickstepLauncher launcher, float velocity,
boolean animateOverviewScrim, @Nullable View ignoredView, boolean staggerWorkspace) {
+ mTaskbarDurationInMs = QuickstepTransitionManager.getTaskbarToHomeDuration(
+ DisplayController.isPinnedTaskbar(launcher));
prepareToAnimate(launcher, animateOverviewScrim);
mIgnoredView = ignoredView;
@@ -93,7 +95,7 @@
.getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y);
DeviceProfile grid = launcher.getDeviceProfile();
- long duration = grid.isTaskbarPresent ? DURATION_TASKBAR_MS : DURATION_MS;
+ long duration = grid.isTaskbarPresent ? mTaskbarDurationInMs : DURATION_MS;
if (staggerWorkspace) {
Workspace<?> workspace = launcher.getWorkspace();
Hotseat hotseat = launcher.getHotseat();
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 9a8041b..6ab3e28 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -610,6 +610,8 @@
private int mKeyboardTaskFocusSnapAnimationDuration;
private int mKeyboardTaskFocusIndex = INVALID_PAGE;
+ private int[] mDismissPrimaryTranslations;
+
/**
* TODO: Call reloadIdNeeded in onTaskStackChanged.
*/
@@ -847,6 +849,8 @@
private final RecentsViewModelHelper mHelper;
private final RecentsViewUtils mUtils = new RecentsViewUtils();
+ private final Matrix mTmpMatrix = new Matrix();
+
public RecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
BaseContainerInterface sizeStrategy) {
super(context, attrs, defStyleAttr);
@@ -1416,7 +1420,7 @@
if (showAsGrid()) {
int screenStart = getPagedOrientationHandler().getPrimaryScroll(this);
int screenEnd = screenStart + getPagedOrientationHandler().getMeasuredSize(this);
- return isTaskViewWithinBounds(tv, screenStart, screenEnd);
+ return isTaskViewWithinBounds(tv, screenStart, screenEnd, /*taskViewTranslation=*/ 0);
} else {
// For now, just check if it's the active task or an adjacent task
return Math.abs(indexOfChild(tv) - getNextPage()) <= 1;
@@ -1463,14 +1467,28 @@
return clearAllScroll + (mIsRtl ? distance : -distance);
}
- private boolean isTaskViewWithinBounds(TaskView tv, int start, int end) {
- int taskStart = getPagedOrientationHandler().getChildStart(tv)
- + (int) tv.getOffsetAdjustment(showAsGrid());
- int taskSize = (int) (getPagedOrientationHandler().getMeasuredSize(tv)
- * tv.getSizeAdjustment(showAsFullscreen()));
+ /*
+ * Returns if TaskView is within screen bounds defined in [screenStart, screenEnd].
+ *
+ * @param taskViewTranslation taskView is considered within bounds if either translated or
+ * original position of taskView is within screen bounds.
+ */
+ private boolean isTaskViewWithinBounds(TaskView taskView, int screenStart, int screenEnd,
+ int taskViewTranslation) {
+ int taskStart = getPagedOrientationHandler().getChildStart(taskView)
+ + (int) taskView.getOffsetAdjustment(showAsGrid());
+ int taskSize = (int) (getPagedOrientationHandler().getMeasuredSize(taskView)
+ * taskView.getSizeAdjustment(showAsFullscreen()));
int taskEnd = taskStart + taskSize;
- return (taskStart >= start && taskStart <= end) || (taskEnd >= start
- && taskEnd <= end);
+
+ int translatedTaskStart = taskStart + taskViewTranslation;
+ int translatedTaskEnd = taskEnd + taskViewTranslation;
+
+ taskStart = Math.min(taskStart, translatedTaskStart);
+ taskEnd = Math.max(taskEnd, translatedTaskEnd);
+
+ return (taskStart >= screenStart && taskStart <= screenEnd) || (taskEnd >= screenStart
+ && taskEnd <= screenEnd);
}
private boolean isTaskViewFullyWithinBounds(TaskView tv, int start, int end) {
@@ -1966,7 +1984,7 @@
// We try to avoid this because it can cause a scroll jump, but it is needed
// for cases where the running task isn't included in this load plan (e.g. if
// the current running task is excludedFromRecents.)
- showCurrentTask(mActiveGestureRunningTasks);
+ showCurrentTask(mActiveGestureRunningTasks, "applyLoadPlan");
} else {
setRunningTaskViewId(INVALID_TASK_ID);
}
@@ -2468,7 +2486,8 @@
}
boolean visible;
if (showAsGrid()) {
- visible = isTaskViewWithinBounds(taskView, visibleStart, visibleEnd);
+ visible = isTaskViewWithinBounds(taskView, visibleStart, visibleEnd,
+ mDismissPrimaryTranslations != null ? mDismissPrimaryTranslations[i] : 0);
} else {
visible = lower <= i && i <= upper;
}
@@ -2749,7 +2768,7 @@
updateSizeAndPadding();
}
- showCurrentTask(mActiveGestureRunningTasks);
+ showCurrentTask(mActiveGestureRunningTasks, "onGestureAnimationStart");
setEnableFreeScroll(false);
setEnableDrawingLiveTile(false);
setRunningTaskHidden(true);
@@ -2930,8 +2949,9 @@
* All subsequent calls to reload will keep the task as the first item until {@link #reset()}
* is called. Also scrolls the view to this task.
*/
- private void showCurrentTask(Task[] runningTasks) {
- Log.d(TAG, "showCurrentTask - runningTasks: " + Arrays.toString(runningTasks));
+ private void showCurrentTask(Task[] runningTasks, String caller) {
+ Log.d(TAG, "showCurrentTask(" + caller + ") - runningTasks: "
+ + Arrays.toString(runningTasks));
if (runningTasks.length == 0) {
return;
}
@@ -3607,7 +3627,8 @@
* @param dismissingForSplitSelection task dismiss animation is used for entering split
* selection state from app icon
*/
- public void createTaskDismissAnimation(PendingAnimation anim, TaskView dismissedTaskView,
+ public void createTaskDismissAnimation(PendingAnimation anim,
+ @Nullable TaskView dismissedTaskView,
boolean animateTaskView, boolean shouldRemoveTask, long duration,
boolean dismissingForSplitSelection) {
if (mPendingAnimation != null) {
@@ -3622,7 +3643,8 @@
boolean showAsGrid = showAsGrid();
int taskCount = getTaskViewCount();
int dismissedIndex = indexOfChild(dismissedTaskView);
- int dismissedTaskViewId = dismissedTaskView.getTaskViewId();
+ int dismissedTaskViewId =
+ dismissedTaskView != null ? dismissedTaskView.getTaskViewId() : INVALID_TASK_ID;
// Grid specific properties.
boolean isFocusedTaskDismissed = false;
@@ -3637,10 +3659,18 @@
int scrollDiffPerPage = 0;
// Non-grid specific properties.
boolean needsCurveUpdates = false;
+ boolean areAllDesktopTasksDismissed = false;
if (showAsGrid) {
- dismissedTaskWidth = dismissedTaskView.getLayoutParams().width + mPageSpacing;
- isFocusedTaskDismissed = dismissedTaskViewId == mFocusedTaskViewId;
+ if (dismissedTaskView != null) {
+ dismissedTaskWidth = dismissedTaskView.getLayoutParams().width + mPageSpacing;
+ }
+ isFocusedTaskDismissed = dismissedTaskViewId != INVALID_TASK_ID
+ && dismissedTaskViewId == mFocusedTaskViewId;
+ if (dismissingForSplitSelection && getTaskViewAt(
+ mCurrentPage) instanceof DesktopTaskView) {
+ areAllDesktopTasksDismissed = true;
+ }
if (isFocusedTaskDismissed) {
if (isSplitSelectionActive()) {
isStagingFocusedTask = true;
@@ -3684,31 +3714,43 @@
int currentPageScroll = getScrollForPage(mCurrentPage);
int lastGridTaskScroll = getScrollForPage(indexOfChild(lastGridTaskView));
boolean currentPageSnapsToEndOfGrid = currentPageScroll == lastGridTaskScroll;
+
+ int topGridRowSize = mTopRowIdSet.size();
+ int numLargeTiles = mUtils.getLargeTileCount(getTaskViews());
+ int bottomGridRowSize = taskCount - mTopRowIdSet.size() - numLargeTiles;
+ boolean topRowLonger = topGridRowSize > bottomGridRowSize;
+ boolean bottomRowLonger = bottomGridRowSize > topGridRowSize;
+ boolean dismissedTaskFromTop = mTopRowIdSet.contains(dismissedTaskViewId);
+ boolean dismissedTaskFromBottom = !dismissedTaskFromTop && !isFocusedTaskDismissed;
+ if (dismissedTaskFromTop || (isFocusedTaskDismissed && nextFocusedTaskFromTop)) {
+ topGridRowSize--;
+ }
+ if (dismissedTaskFromBottom || (isFocusedTaskDismissed && !nextFocusedTaskFromTop)) {
+ bottomGridRowSize--;
+ }
+ int longRowWidth = Math.max(topGridRowSize, bottomGridRowSize)
+ * (mLastComputedGridTaskSize.width() + mPageSpacing);
+ if (!enableGridOnlyOverview() && !isStagingFocusedTask) {
+ longRowWidth += mLastComputedTaskSize.width() + mPageSpacing;
+ }
+ // Compensate the removed gap if we don't already have shortTotalCompensation,
+ // and adjust accordingly to the new shortTotalCompensation after dismiss.
+ int newClearAllShortTotalWidthTranslation = 0;
+ if (mClearAllShortTotalWidthTranslation == 0) {
+ // If first task is not in the expected position (mLastComputedTaskSize) and being too
+ // close to ClearAllButton, then apply extra translation to ClearAllButton.
+ int firstTaskStart = mLastComputedGridSize.left + longRowWidth;
+ int expectedFirstTaskStart = mLastComputedTaskSize.right;
+ if (firstTaskStart < expectedFirstTaskStart) {
+ newClearAllShortTotalWidthTranslation = expectedFirstTaskStart - firstTaskStart;
+ }
+ }
if (lastGridTaskView != null && lastGridTaskView.isVisibleToUser()) {
// After dismissal, animate translation of the remaining tasks to fill any gap left
// between the end of the grid and the clear all button. Only animate if the clear
// all button is visible or would become visible after dismissal.
float longGridRowWidthDiff = 0;
- int topGridRowSize = mTopRowIdSet.size();
- int numLargeTiles = mUtils.getLargeTileCount(getTaskViews());
- int bottomGridRowSize = taskCount - mTopRowIdSet.size() - numLargeTiles;
- boolean topRowLonger = topGridRowSize > bottomGridRowSize;
- boolean bottomRowLonger = bottomGridRowSize > topGridRowSize;
- boolean dismissedTaskFromTop = mTopRowIdSet.contains(dismissedTaskViewId);
- boolean dismissedTaskFromBottom = !dismissedTaskFromTop && !isFocusedTaskDismissed;
- if (dismissedTaskFromTop || (isFocusedTaskDismissed && nextFocusedTaskFromTop)) {
- topGridRowSize--;
- }
- if (dismissedTaskFromBottom || (isFocusedTaskDismissed && !nextFocusedTaskFromTop)) {
- bottomGridRowSize--;
- }
- int longRowWidth = Math.max(topGridRowSize, bottomGridRowSize)
- * (mLastComputedGridTaskSize.width() + mPageSpacing);
- if (!enableGridOnlyOverview() && !isStagingFocusedTask) {
- longRowWidth += mLastComputedTaskSize.width() + mPageSpacing;
- }
-
float gapWidth = 0;
if ((topRowLonger && dismissedTaskFromTop)
|| (bottomRowLonger && dismissedTaskFromBottom)) {
@@ -3720,17 +3762,6 @@
}
if (gapWidth > 0) {
if (mClearAllShortTotalWidthTranslation == 0) {
- // Compensate the removed gap if we don't already have shortTotalCompensation,
- // and adjust accordingly to the new shortTotalCompensation after dismiss.
- int newClearAllShortTotalWidthTranslation = 0;
- if (longRowWidth < mLastComputedGridSize.width()) {
- DeviceProfile deviceProfile = mContainer.getDeviceProfile();
- newClearAllShortTotalWidthTranslation =
- (mIsRtl
- ? mLastComputedTaskSize.right
- : deviceProfile.widthPx - mLastComputedTaskSize.left)
- - longRowWidth - deviceProfile.overviewGridSideMargin;
- }
float gapCompensation = gapWidth - newClearAllShortTotalWidthTranslation;
longGridRowWidthDiff += mIsRtl ? -gapCompensation : gapCompensation;
}
@@ -3812,19 +3843,26 @@
SplitAnimationTimings splitTimings =
AnimUtils.getDeviceOverviewToSplitTimings(mContainer.getDeviceProfile().isTablet);
- int distanceFromDismissedTask = 0;
+ int distanceFromDismissedTask = 1;
+ int stagingTranslation = 0;
+ if (isStagingFocusedTask || areAllDesktopTasksDismissed) {
+ int nextSnappedPage = isStagingFocusedTask
+ ? indexOfChild(mUtils.getFirstSmallTaskView(getTaskViews()))
+ : mUtils.getDesktopTaskViewCount(getTaskViews());
+ stagingTranslation = getPagedOrientationHandler().getPrimaryScroll(this)
+ - getScrollForPage(nextSnappedPage);
+ stagingTranslation += mIsRtl ? newClearAllShortTotalWidthTranslation
+ : -newClearAllShortTotalWidthTranslation;
+ }
+ mDismissPrimaryTranslations = new int[taskCount];
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child == dismissedTaskView) {
- if (animateTaskView) {
- if (dismissingForSplitSelection) {
- createInitialSplitSelectAnimation(anim);
- } else {
- addDismissedTaskAnimations(dismissedTaskView, duration, anim);
- }
+ if (animateTaskView && !dismissingForSplitSelection) {
+ addDismissedTaskAnimations(dismissedTaskView, duration, anim);
}
} else if (!showAsGrid || (enableLargeDesktopWindowingTile()
- && dismissedTaskView.isLargeTile()
+ && dismissedTaskView != null && dismissedTaskView.isLargeTile()
&& nextFocusedTaskView == null && !dismissingForSplitSelection)) {
int offset = getOffsetToDismissedTask(scrollDiffPerPage, dismissedIndex, taskCount);
int scrollDiff = newScroll[i] - oldScroll[i] + offset;
@@ -3834,23 +3872,13 @@
Math.abs(i - dismissedIndex),
scrollDiff,
anim,
- splitTimings);
+ splitTimings, i);
needsCurveUpdates = true;
}
} else if (child instanceof TaskView taskView) {
- if (isFocusedTaskDismissed) {
- if (nextFocusedTaskView != null &&
- !isSameGridRow(taskView, nextFocusedTaskView)) {
- continue;
- }
- } else if (i < dismissedIndex || !isSameGridRow(taskView, dismissedTaskView)) {
- continue;
- }
-
// Animate task with index >= dismissed index and in the same row as the
// dismissed index or next focused index. Offset successive task dismissal
// durations for a staggered effect.
- distanceFromDismissedTask++;
int staggerColumn = isStagingFocusedTask
? (int) Math.ceil(distanceFromDismissedTask / 2f)
: distanceFromDismissedTask;
@@ -3877,16 +3905,15 @@
: dismissTranslationInterpolationEnd;
Interpolator dismissInterpolator = isStagingFocusedTask ? OVERSHOOT_0_75 : LINEAR;
+ float primaryTranslation = 0;
if (taskView == nextFocusedTaskView) {
// Enlarge the task to be focused next, and translate into focus position.
float scale = mTaskWidth / (float) mLastComputedGridTaskSize.width();
anim.setFloat(taskView, TaskView.DISMISS_SCALE, scale,
clampToProgress(LINEAR, animationStartProgress,
dismissTranslationInterpolationEnd));
- anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
- mIsRtl ? dismissedTaskWidth : -dismissedTaskWidth,
- clampToProgress(LINEAR, animationStartProgress,
- dismissTranslationInterpolationEnd));
+ primaryTranslation += dismissedTaskWidth;
+ animationEndProgress = dismissTranslationInterpolationEnd;
float secondaryTranslation = -mTaskGridVerticalDiff;
if (!nextFocusedTaskFromTop) {
secondaryTranslation -= mTopBottomRowHeightDiff;
@@ -3896,25 +3923,29 @@
dismissTranslationInterpolationEnd));
anim.add(taskView.getFocusTransitionScaleAndDimOutAnimator(),
clampToProgress(LINEAR, 0f, ANIMATION_DISMISS_PROGRESS_MIDPOINT));
- } else {
- float primaryTranslation =
+ } else if ((isFocusedTaskDismissed && nextFocusedTaskView != null && isSameGridRow(
+ taskView, nextFocusedTaskView))
+ || (!isFocusedTaskDismissed && i >= dismissedIndex && isSameGridRow(
+ taskView, dismissedTaskView))) {
+ primaryTranslation +=
nextFocusedTaskView != null ? nextFocusedTaskWidth : dismissedTaskWidth;
- if (isStagingFocusedTask) {
- // Moves less if focused task is not in scroll position.
- int focusedTaskScroll = getScrollForPage(dismissedIndex);
- int primaryScroll = getPagedOrientationHandler().getPrimaryScroll(this);
- int focusedTaskScrollDiff = primaryScroll - focusedTaskScroll;
- primaryTranslation +=
- mIsRtl ? focusedTaskScrollDiff : -focusedTaskScrollDiff;
- }
+ }
+ primaryTranslation += mIsRtl ? stagingTranslation : -stagingTranslation;
+ if (primaryTranslation != 0) {
+ float finalTranslation = mIsRtl ? primaryTranslation : -primaryTranslation;
anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
- mIsRtl ? primaryTranslation : -primaryTranslation,
+ finalTranslation,
clampToProgress(dismissInterpolator, animationStartProgress,
animationEndProgress));
+ mDismissPrimaryTranslations[i] = (int) finalTranslation;
+ distanceFromDismissedTask++;
}
}
}
+ if (dismissingForSplitSelection) {
+ createInitialSplitSelectAnimation(anim);
+ }
if (needsCurveUpdates) {
anim.addOnFrameCallback(this::updateCurveProperties);
@@ -3923,10 +3954,10 @@
// Add a tiny bit of translation Z, so that it draws on top of other views. This is relevant
// (e.g.) when we dismiss a task by sliding it upward: if there is a row of icons above, we
// want the dragged task to stay above all other views.
- if (animateTaskView) {
+ if (animateTaskView && dismissedTaskView != null) {
dismissedTaskView.setTranslationZ(0.1f);
}
-
+ loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
mPendingAnimation = anim;
final TaskView finalNextFocusedTaskView = nextFocusedTaskView;
final boolean finalCloseGapBetweenClearAll = closeGapBetweenClearAll;
@@ -3935,7 +3966,8 @@
mPendingAnimation.addEndListener(new Consumer<>() {
@Override
public void accept(Boolean success) {
- if (mEnableDrawingLiveTile && dismissedTaskView.isRunningTask() && success) {
+ if (mEnableDrawingLiveTile && dismissedTaskView != null
+ && dismissedTaskView.isRunningTask() && success) {
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
() -> onEnd(true));
} else {
@@ -3951,7 +3983,7 @@
if (success) {
mAnyTaskHasBeenDismissed = true;
- if (shouldRemoveTask) {
+ if (shouldRemoveTask && dismissedTaskView != null) {
if (dismissedTaskView.isRunningTask()) {
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
() -> removeTaskInternal(dismissedTaskView));
@@ -4143,6 +4175,7 @@
updateCurrentTaskActionsVisibility();
onDismissAnimationEnds();
mPendingAnimation = null;
+ mDismissPrimaryTranslations = null;
}
});
}
@@ -4181,7 +4214,8 @@
int indexDiff,
int scrollDiffPerPage,
PendingAnimation pendingAnimation,
- SplitAnimationTimings splitTimings) {
+ SplitAnimationTimings splitTimings,
+ int index) {
FloatProperty translationProperty = view instanceof TaskView
? ((TaskView) view).getPrimaryDismissTranslationProperty()
: getPagedOrientationHandler().getPrimaryViewTranslate();
@@ -4215,6 +4249,9 @@
)
);
+ if (view instanceof TaskView) {
+ mDismissPrimaryTranslations[index] = scrollDiffPerPage;
+ }
if (mEnableDrawingLiveTile && view instanceof TaskView
&& ((TaskView) view).isRunningTask()) {
pendingAnimation.addOnFrameCallback(() -> {
@@ -5080,9 +5117,10 @@
if (enableLargeDesktopWindowingTile()) {
for (TaskView taskView : getTaskViews()) {
if (taskView instanceof DesktopTaskView) {
+ // Correcting the animation for split mode since we hide DW in split.
builder.addFloat(taskView.getSplitAlphaProperty(),
MULTI_PROPERTY_VALUE, 1f, 0f,
- deskTopFadeInterPolator);
+ clampToProgress(deskTopFadeInterPolator, 0f, 0.1f));
}
}
}
@@ -5140,7 +5178,15 @@
true /* dismissingForSplitSelection*/);
} else {
// Splitting from Home
- createInitialSplitSelectAnimation(builder);
+ TaskView currentPageTaskView = getTaskViewAt(mCurrentPage);
+ // When current page is a Desktop task it needs special handling to
+ // display correct animation in split mode
+ if (currentPageTaskView instanceof DesktopTaskView) {
+ createTaskDismissAnimation(builder, null, true, false, duration,
+ true /* dismissingForSplitSelection*/);
+ } else {
+ createInitialSplitSelectAnimation(builder);
+ }
}
}
@@ -5789,6 +5835,14 @@
// mSyncTransactionApplier doesn't get transferred over
runActionOnRemoteHandles(remoteTargetHandle -> {
final TransformParams params = remoteTargetHandle.getTransformParams();
+ if (Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow()) {
+ params.setHomeBuilderProxy((builder, app, transformParams) -> {
+ mTmpMatrix.setScale(
+ 1f, 1f, app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
+ builder.setMatrix(mTmpMatrix).setAlpha(1f).setShow();
+ });
+ }
+
if (mSyncTransactionApplier != null) {
params.setSyncTransactionApplier(mSyncTransactionApplier);
params.getTargetSet().addReleaseCheck(mSyncTransactionApplier);
@@ -5825,15 +5879,22 @@
* Finish recents animation.
*/
public void finishRecentsAnimation(boolean toRecents, @Nullable Runnable onFinishComplete) {
- finishRecentsAnimation(toRecents, true /* shouldPip */, onFinishComplete);
+ finishRecentsAnimation(toRecents, false, true /* shouldPip */, onFinishComplete);
}
/**
+ * Finish recents animation.
+ */
+ public void finishRecentsAnimation(boolean toRecents, boolean shouldPip,
+ @Nullable Runnable onFinishComplete) {
+ finishRecentsAnimation(toRecents, shouldPip, false, onFinishComplete);
+ }
+ /**
* NOTE: Whatever value gets passed through to the toRecents param may need to also be set on
* {@link #mRecentsAnimationController#setWillFinishToHome}.
*/
public void finishRecentsAnimation(boolean toRecents, boolean shouldPip,
- @Nullable Runnable onFinishComplete) {
+ boolean allAppTargetsAreTranslucent, @Nullable Runnable onFinishComplete) {
Log.d(TAG, "finishRecentsAnimation - mRecentsAnimationController: "
+ mRecentsAnimationController);
// TODO(b/197232424#comment#10) Move this back into onRecentsAnimationComplete(). Maybe?
@@ -5865,7 +5926,7 @@
tx, null /* overlay */);
}
}
- mRecentsAnimationController.finish(toRecents, () -> {
+ mRecentsAnimationController.finish(toRecents, allAppTargetsAreTranslucent, () -> {
if (onFinishComplete != null) {
onFinishComplete.run();
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt b/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
index f22c672..3616fbb 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
@@ -25,6 +25,7 @@
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
/** Helper for [RecentsView] to interact with the [RecentsViewModel]. */
class RecentsViewModelHelper(private val recentsViewModel: RecentsViewModel) {
@@ -32,7 +33,7 @@
fun onAttachedToWindow() {
viewAttachedScope =
- CoroutineScope(SupervisorJob() + Dispatchers.Main + CoroutineName("RecentsView"))
+ CoroutineScope(SupervisorJob() + Dispatchers.Default + CoroutineName("RecentsView"))
}
fun onDetachedFromWindow() {
@@ -50,7 +51,7 @@
viewAttachedScope.launch {
recentsViewModel.waitForRunningTaskShowScreenshotToUpdate()
recentsViewModel.waitForThumbnailsToUpdate(updatedThumbnails)
- ViewUtils.postFrameDrawn(taskView, onFinishRunnable)
+ withContext(Dispatchers.Main) { ViewUtils.postFrameDrawn(taskView, onFinishRunnable) }
}
}
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
index 25aba39..c940fb4 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -110,7 +110,6 @@
return snapshotView as TaskThumbnailViewDeprecated
}
- // TODO(b/334826842): Support shouldShowSplashView for new TTV.
val shouldShowSplashView: Boolean
get() =
if (enableRefactorTaskThumbnail())
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 28ecf96..b1cb407 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -636,24 +636,29 @@
override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {
super.onInitializeAccessibilityNodeInfo(info)
with(info) {
- addAction(
- AccessibilityAction(
- R.id.action_close,
- context.getText(R.string.accessibility_close),
+ // Only make actions available if the app icon menu is visible to the user.
+ // When modalness is >0, the user is in select mode and the icon menu is hidden.
+ if (modalness == 0f) {
+ addAction(
+ AccessibilityAction(
+ R.id.action_close,
+ context.getText(R.string.accessibility_close),
+ )
)
- )
- taskContainers.forEach {
- TraceHelper.allowIpcs("TV.a11yInfo") {
- TaskOverlayFactory.getEnabledShortcuts(this@TaskView, it).forEach { shortcut ->
- addAction(shortcut.createAccessibilityAction(context))
+ taskContainers.forEach {
+ TraceHelper.allowIpcs("TV.a11yInfo") {
+ TaskOverlayFactory.getEnabledShortcuts(this@TaskView, it).forEach { shortcut
+ ->
+ addAction(shortcut.createAccessibilityAction(context))
+ }
}
}
- }
- // Add DWB accessibility action at the end of the list
- taskContainers.forEach {
- it.digitalWellBeingToast?.getDWBAccessibilityAction()?.let(::addAction)
+ // Add DWB accessibility action at the end of the list
+ taskContainers.forEach {
+ it.digitalWellBeingToast?.getDWBAccessibilityAction()?.let(::addAction)
+ }
}
recentsView?.let {
@@ -1216,8 +1221,6 @@
if (isQuickSwitch) {
setFreezeRecentTasksReordering()
}
- // TODO(b/334826842) no work required - add splash functionality to new TTV -
- // cold start e.g. restart device. Small splash moving to bigger splash
disableStartingWindow = firstContainer.shouldShowSplashView
}
Executors.UI_HELPER_EXECUTOR.execute {
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
index 436dfd3..4c94067 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
@@ -17,10 +17,14 @@
package com.android.launcher3.taskbar
import android.animation.AnimatorTestRule
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.view.KeyEvent
import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
import com.android.launcher3.taskbar.rules.TaskbarModeRule
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.PINNED
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.TRANSIENT
@@ -34,6 +38,7 @@
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE
+import com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR
import com.android.wm.shell.shared.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -43,7 +48,8 @@
@RunWith(LauncherMultivalentJUnit::class)
@EmulatedDevices(["pixelTablet2023"])
class TaskbarScrimViewControllerTest {
- @get:Rule(order = 0)
+ @get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
+ @get:Rule(order = 1)
val context =
TaskbarWindowSandboxContext.create { builder ->
builder.bindSystemUiProxy(
@@ -55,9 +61,9 @@
}
)
}
- @get:Rule(order = 1) val taskbarModeRule = TaskbarModeRule(context)
- @get:Rule(order = 2) val animatorTestRule = AnimatorTestRule(this)
- @get:Rule(order = 3) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+ @get:Rule(order = 2) val taskbarModeRule = TaskbarModeRule(context)
+ @get:Rule(order = 3) val animatorTestRule = AnimatorTestRule(this)
+ @get:Rule(order = 4) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
@InjectController lateinit var scrimViewController: TaskbarScrimViewController
@@ -90,6 +96,7 @@
}
@Test
+ @DisableFlags(FLAG_ENABLE_BUBBLE_BAR)
@TaskbarMode(PINNED)
fun testOnTaskbarVisibilityChanged_pinnedTaskbarHiddenDuringScrim_hidesScrim() {
getInstrumentation().runOnMainSync {
@@ -106,6 +113,26 @@
}
@Test
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @TaskbarMode(PINNED)
+ fun testOnTaskbarVisibilityChanged_pinnedTaskbarOnHomeHiddenDuringScrim_hidesScrim() {
+ getInstrumentation().runOnMainSync {
+ scrimViewController.onTaskbarVisibilityChanged(VISIBLE)
+ taskbarUnitTestRule.activityContext.bubbleControllers!!
+ .bubbleStashController
+ .launcherState = BubbleStashController.BubbleLauncherState.HOME
+ scrimViewController.updateStateForSysuiFlags(SYSUI_STATE_BUBBLES_EXPANDED, true)
+ }
+ assertThat(scrimViewController.scrimAlpha).isEqualTo(BUBBLE_EXPANDED_SCRIM_ALPHA)
+
+ getInstrumentation().runOnMainSync {
+ scrimViewController.onTaskbarVisibilityChanged(GONE)
+ animatorTestRule.advanceTimeBy(animationDuration)
+ }
+ assertThat(scrimViewController.scrimAlpha).isEqualTo(0)
+ }
+
+ @Test
@TaskbarMode(PINNED)
fun testOnTaskbarVisibilityChanged_notificationsOverPinnedTaskbarAndBubbles_noScrim() {
getInstrumentation().runOnMainSync {
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
index 71f4ef4..5e438bd 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
@@ -21,6 +21,7 @@
import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
+import com.android.launcher3.QuickstepTransitionManager.PINNED_TASKBAR_TRANSITION_DURATION
import com.android.launcher3.R
import com.android.launcher3.taskbar.StashedHandleViewController.ALPHA_INDEX_STASHED
import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_EDU_OPEN
@@ -158,7 +159,7 @@
@Test
@TaskbarMode(PINNED)
fun testGetStashDuration_pinnedMode() {
- assertThat(stashController.stashDuration).isEqualTo(TASKBAR_STASH_DURATION)
+ assertThat(stashController.stashDuration).isEqualTo(PINNED_TASKBAR_TRANSITION_DURATION)
}
@Test
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
index 48f3fc2..582ea54 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
@@ -1123,7 +1123,7 @@
animator.animateBubbleInForStashed(updatedBubble, isExpanding = false)
// the flyout should now reverse and expand
- animatorTestRule.advanceTimeBy(100)
+ animatorTestRule.advanceTimeBy(400)
}
assertThat(flyoutView!!.findViewById<TextView>(R.id.bubble_flyout_text).text)
@@ -1362,21 +1362,21 @@
private fun waitForFlyoutToShow() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
- animatorTestRule.advanceTimeBy(250)
+ animatorTestRule.advanceTimeBy(400)
}
assertThat(flyoutView).isNotNull()
}
private fun waitForFlyoutToHide() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
- animatorTestRule.advanceTimeBy(250)
+ animatorTestRule.advanceTimeBy(350)
}
assertThat(flyoutView).isNull()
}
private fun waitForFlyoutToFadeOutAndBackIn() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
- animatorTestRule.advanceTimeBy(500)
+ animatorTestRule.advanceTimeBy(750)
}
assertThat(flyoutView).isNotNull()
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
index 2997ac9..103c769 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
@@ -50,6 +50,9 @@
private var onLeft = true
private var flyoutTy = 50f
+ private val showAnimationDuration = 400L
+ private val hideAnimationDuration = 350L
+
@Before
fun setUp() {
flyoutContainer = FrameLayout(context)
@@ -118,7 +121,7 @@
assertThat(flyoutController.hasFlyout()).isTrue()
assertThat(flyoutContainer.childCount).isEqualTo(1)
flyoutController.collapseFlyout {}
- animatorTestRule.advanceTimeBy(300)
+ animatorTestRule.advanceTimeBy(hideAnimationDuration)
}
assertThat(flyoutContainer.childCount).isEqualTo(0)
assertThat(flyoutController.hasFlyout()).isFalse()
@@ -135,7 +138,7 @@
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
- animatorTestRule.advanceTimeBy(300)
+ animatorTestRule.advanceTimeBy(showAnimationDuration)
}
assertThat(flyoutCallbacks.topBoundaryExtendedSpace).isEqualTo(50)
}
@@ -148,7 +151,7 @@
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
- animatorTestRule.advanceTimeBy(300)
+ animatorTestRule.advanceTimeBy(showAnimationDuration)
}
assertThat(flyoutCallbacks.topBoundaryExtendedSpace).isEqualTo(0)
}
@@ -159,7 +162,7 @@
setupAndShowFlyout()
assertThat(flyoutContainer.childCount).isEqualTo(1)
flyoutController.collapseFlyout {}
- animatorTestRule.advanceTimeBy(300)
+ animatorTestRule.advanceTimeBy(hideAnimationDuration)
}
assertThat(flyoutCallbacks.topBoundaryReset).isTrue()
}
@@ -172,7 +175,7 @@
val flyoutView = flyoutContainer.findViewById<View>(R.id.bubble_bar_flyout_view)
assertThat(flyoutView.alpha).isEqualTo(1f)
flyoutController.cancelFlyout {}
- animatorTestRule.advanceTimeBy(300)
+ animatorTestRule.advanceTimeBy(hideAnimationDuration)
assertThat(flyoutView.alpha).isEqualTo(0f)
}
assertThat(flyoutCallbacks.topBoundaryReset).isTrue()
@@ -185,7 +188,7 @@
assertThat(flyoutContainer.childCount).isEqualTo(1)
val flyoutView = flyoutContainer.findViewById<View>(R.id.bubble_bar_flyout_view)
assertThat(flyoutView.alpha).isEqualTo(1f)
- animatorTestRule.advanceTimeBy(300)
+ animatorTestRule.advanceTimeBy(showAnimationDuration)
flyoutView.performClick()
}
assertThat(flyoutCallbacks.flyoutClicked).isTrue()
@@ -221,7 +224,7 @@
fun updateFlyoutFullyExpanded() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
setupAndShowFlyout()
- animatorTestRule.advanceTimeBy(300)
+ animatorTestRule.advanceTimeBy(showAnimationDuration)
}
assertThat(flyoutController.hasFlyout()).isTrue()
@@ -234,13 +237,13 @@
flyoutController.updateFlyoutFullyExpanded(newFlyoutMessage) {}
// advance the timer so that the fade out animation plays
- animatorTestRule.advanceTimeBy(250)
+ animatorTestRule.advanceTimeBy(hideAnimationDuration)
assertThat(flyout.alpha).isEqualTo(0)
assertThat(flyout.findViewById<TextView>(R.id.bubble_flyout_text).text)
.isEqualTo("new message")
// advance the timer so that the fade in animation plays
- animatorTestRule.advanceTimeBy(250)
+ animatorTestRule.advanceTimeBy(showAnimationDuration)
assertThat(flyout.alpha).isEqualTo(1)
}
assertThat(flyoutCallbacks.topBoundaryExtendedSpace).isEqualTo(50)
@@ -250,7 +253,7 @@
fun updateFlyoutWhileCollapsing() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
setupAndShowFlyout()
- animatorTestRule.advanceTimeBy(300)
+ animatorTestRule.advanceTimeBy(showAnimationDuration)
}
assertThat(flyoutController.hasFlyout()).isTrue()
@@ -265,9 +268,10 @@
var flyoutReversed = false
flyoutController.updateFlyoutWhileCollapsing(newFlyoutMessage) { flyoutReversed = true }
- // the collapse animation ran for 125ms when it was updated, so reversing it should only
- // run for the same amount of time
- animatorTestRule.advanceTimeBy(125)
+ // the collapse and expand animations use an emphasized interpolator, so the reverse
+ // path does not take the same time. advance the timer the by full duration of the show
+ // animation to ensure it completes
+ animatorTestRule.advanceTimeBy(showAnimationDuration)
val flyout = flyoutContainer.findViewById<View>(R.id.bubble_bar_flyout_view)
assertThat(flyout.alpha).isEqualTo(1)
assertThat(flyout.findViewById<TextView>(R.id.bubble_flyout_text).text)
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
index 00b42bc..f795ab1 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
@@ -38,6 +38,7 @@
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@@ -85,6 +86,20 @@
}
@Test
+ fun updateLauncherState_noBubbles_controllerNotified() {
+ // Given bubble bar has no bubbles
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+
+ // When switch to home screen
+ getInstrumentation().runOnMainSync {
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.HOME
+ }
+
+ // Then bubble bar view controller is notified
+ verify(bubbleBarViewController).onBubbleBarConfigurationChanged(/* animate= */ false)
+ }
+
+ @Test
fun setBubblesShowingOnHomeUpdatedToFalse_barPositionYUpdated_controllersNotified() {
// Given bubble bar is on home and has bubbles
whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
@@ -228,6 +243,105 @@
.isEqualTo(TASK_BAR_TRANSLATION_Y)
}
+ @Test
+ fun inAppDisplayOverrideProgress_onHome_updatesTranslationFromHomeToInApp() {
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.HOME
+
+ assertThat(persistentTaskBarStashController.bubbleBarTranslationY)
+ .isEqualTo(HOTSEAT_TRANSLATION_Y)
+
+ persistentTaskBarStashController.inAppDisplayOverrideProgress = 0.5f
+
+ val middleBetweenHotseatAndTaskbar = (HOTSEAT_TRANSLATION_Y + TASK_BAR_TRANSLATION_Y) / 2f
+ assertThat(persistentTaskBarStashController.bubbleBarTranslationY)
+ .isWithin(0.1f)
+ .of(middleBetweenHotseatAndTaskbar)
+
+ persistentTaskBarStashController.inAppDisplayOverrideProgress = 1f
+
+ assertThat(persistentTaskBarStashController.bubbleBarTranslationY)
+ .isEqualTo(TASK_BAR_TRANSLATION_Y)
+ }
+
+ @Test
+ fun inAppDisplayOverrideProgress_onHome_updatesInsetsWhenProgressReachesOne() {
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.HOME
+ // Reset invocations to track only changes from in-app display override
+ clearInvocations(taskbarInsetsController)
+
+ // Insets are not updated for values between 0 and 1
+ persistentTaskBarStashController.inAppDisplayOverrideProgress = 0.5f
+ verify(taskbarInsetsController, never()).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+
+ // Update insets when progress reaches 1
+ persistentTaskBarStashController.inAppDisplayOverrideProgress = 1f
+ verify(taskbarInsetsController).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+
+ @Test
+ fun inAppDisplayOverrideProgress_onHome_updatesInsetsWhenProgressReachesZero() {
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.HOME
+ persistentTaskBarStashController.inAppDisplayOverrideProgress = 1f
+ // Reset invocations to track only changes from in-app display override
+ clearInvocations(taskbarInsetsController)
+
+ // Insets are not updated for values between 0 and 1
+ persistentTaskBarStashController.inAppDisplayOverrideProgress = 0.5f
+ verify(taskbarInsetsController, never()).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+
+ // Update insets when progress reaches 0
+ persistentTaskBarStashController.inAppDisplayOverrideProgress = 0f
+ verify(taskbarInsetsController).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+
+ @Test
+ fun inAppDisplayOverrideProgress_onHome_cancelExistingAnimation() {
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.HOME
+
+ bubbleBarViewController.bubbleBarTranslationY.animateToValue(100f)
+ advanceTimeBy(10)
+ assertThat(bubbleBarViewController.bubbleBarTranslationY.isAnimating).isTrue()
+
+ getInstrumentation().runOnMainSync {
+ persistentTaskBarStashController.inAppDisplayOverrideProgress = 0.5f
+ }
+ assertThat(bubbleBarViewController.bubbleBarTranslationY.isAnimating).isFalse()
+ }
+
+ @Test
+ fun inAppDisplayProgressUpdate_inApp_noTranslationUpdate() {
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.IN_APP
+
+ assertThat(persistentTaskBarStashController.bubbleBarTranslationY)
+ .isEqualTo(TASK_BAR_TRANSLATION_Y)
+
+ persistentTaskBarStashController.inAppDisplayOverrideProgress = 0.5f
+
+ assertThat(persistentTaskBarStashController.bubbleBarTranslationY)
+ .isEqualTo(TASK_BAR_TRANSLATION_Y)
+ }
+
+ @Test
+ fun inAppDisplayOverrideProgress_inApp_noInsetsUpdate() {
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.IN_APP
+
+ // Reset invocations to track only changes from in-app display override
+ clearInvocations(taskbarInsetsController)
+
+ persistentTaskBarStashController.inAppDisplayOverrideProgress = 0.5f
+ persistentTaskBarStashController.inAppDisplayOverrideProgress = 1f
+ persistentTaskBarStashController.inAppDisplayOverrideProgress = 0f
+
+ // Never triggers an update to insets
+ verify(taskbarInsetsController, never()).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+
private fun advanceTimeBy(advanceMs: Long) {
// Advance animator for on-device tests
getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(advanceMs) }
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
index 64416dd..1bbd12a 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
@@ -119,6 +119,20 @@
}
@Test
+ fun updateLauncherState_noBubbles_controllerNotified() {
+ // Given bubble bar has no bubbles
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+
+ // When switch to home screen
+ getInstrumentation().runOnMainSync {
+ mTransientBubbleStashController.launcherState = BubbleLauncherState.HOME
+ }
+
+ // Then bubble bar view controller is notified
+ verify(bubbleBarViewController).onBubbleBarConfigurationChanged(/* animate= */ false)
+ }
+
+ @Test
fun setBubblesShowingOnHomeUpdatedToTrue_barPositionYUpdated_controllersNotified() {
// Given bubble bar is on home and has bubbles
whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
index dc5223c..6b95f8d 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
@@ -80,8 +80,6 @@
protected final Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
- protected final RecentsAnimationDeviceState mRecentsAnimationDeviceState =
- new RecentsAnimationDeviceState(mContext, true);
protected final InputConsumerController mInputConsumerController =
InputConsumerController.getRecentsAnimationInputConsumer();
protected final ActivityManager.RunningTaskInfo mRunningTaskInfo =
@@ -114,6 +112,7 @@
new Bundle());
protected TaskAnimationManager mTaskAnimationManager;
+ protected RecentsAnimationDeviceState mRecentsAnimationDeviceState;
@Mock protected CONTAINER_INTERFACE mActivityInterface;
@Mock protected ContextInitListener<?> mContextInitListener;
@@ -176,6 +175,12 @@
}).when(recentsContainer).runOnBindToTouchInteractionService(any());
}
+ @Before
+ public void setUpRecentsAnimationDeviceState() {
+ runOnMainSync(() ->
+ mRecentsAnimationDeviceState = new RecentsAnimationDeviceState(mContext, true));
+ }
+
@Test
public void testInitWhenReady_registersActivityInitListener() {
String reasonString = "because i said so";
@@ -306,8 +311,6 @@
}
private void onRecentsAnimationStart(SWIPE_HANDLER absSwipeUpHandler) {
- when(mActivityInterface.getOverviewWindowBounds(any(), any())).thenReturn(new Rect());
-
runOnMainSync(() -> absSwipeUpHandler.onRecentsAnimationStart(
mRecentsAnimationController, mRecentsAnimationTargets));
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelImplTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelImplTest.kt
index c541d3d..e3a6adf 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelImplTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelImplTest.kt
@@ -24,6 +24,7 @@
import android.graphics.drawable.Drawable
import android.view.Surface
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.util.TestDispatcherProvider
import com.android.quickstep.recents.data.FakeTasksRepository
import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
import com.android.quickstep.recents.usecase.ThumbnailPositionState.MatrixScaling
@@ -42,6 +43,8 @@
import com.android.systemui.shared.recents.model.ThumbnailData
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,18 +54,24 @@
/** Test for [TaskThumbnailView] */
@RunWith(AndroidJUnit4::class)
class TaskThumbnailViewModelImplTest {
+ private val dispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(dispatcher)
+
private var taskViewType = TaskViewType.SINGLE
private val recentsViewData = RecentsViewData()
private val taskViewData by lazy { TaskViewData(taskViewType) }
private val taskContainerData = TaskContainerData()
+ private val dispatcherProvider = TestDispatcherProvider(dispatcher)
private val tasksRepository = FakeTasksRepository()
private val mGetThumbnailPositionUseCase = mock<GetThumbnailPositionUseCase>()
private val splashAlphaUseCase: SplashAlphaUseCase = mock()
+
private val systemUnderTest by lazy {
TaskThumbnailViewModelImpl(
recentsViewData,
taskViewData,
taskContainerData,
+ dispatcherProvider,
tasksRepository,
mGetThumbnailPositionUseCase,
splashAlphaUseCase,
@@ -72,81 +81,85 @@
private val tasks = (0..5).map(::createTaskWithId)
@Test
- fun initialStateIsUninitialized() = runTest {
- assertThat(systemUnderTest.uiState.first()).isEqualTo(Uninitialized)
- }
+ fun initialStateIsUninitialized() =
+ testScope.runTest { assertThat(systemUnderTest.uiState.first()).isEqualTo(Uninitialized) }
@Test
- fun bindRunningTask_thenStateIs_LiveTile() = runTest {
- val taskId = 1
- tasksRepository.seedTasks(tasks)
- tasksRepository.setVisibleTasks(setOf(taskId))
- recentsViewData.runningTaskIds.value = setOf(taskId)
- systemUnderTest.bind(taskId)
+ fun bindRunningTask_thenStateIs_LiveTile() =
+ testScope.runTest {
+ val taskId = 1
+ tasksRepository.seedTasks(tasks)
+ tasksRepository.setVisibleTasks(setOf(taskId))
+ recentsViewData.runningTaskIds.value = setOf(taskId)
+ systemUnderTest.bind(taskId)
- assertThat(systemUnderTest.uiState.first()).isEqualTo(LiveTile)
- }
+ assertThat(systemUnderTest.uiState.first()).isEqualTo(LiveTile)
+ }
@Test
- fun bindRunningTaskShouldShowScreenshot_thenStateIs_SnapshotSplash() = runTest {
- val taskId = 1
- val expectedThumbnailData = createThumbnailData()
- tasksRepository.seedThumbnailData(mapOf(taskId to expectedThumbnailData))
- val expectedIconData = mock<Drawable>()
- tasksRepository.seedIconData(taskId, "Task $taskId", "", expectedIconData)
- tasksRepository.seedTasks(tasks)
- tasksRepository.setVisibleTasks(setOf(taskId))
- recentsViewData.runningTaskIds.value = setOf(taskId)
- recentsViewData.runningTaskShowScreenshot.value = true
- systemUnderTest.bind(taskId)
+ fun bindRunningTaskShouldShowScreenshot_thenStateIs_SnapshotSplash() =
+ testScope.runTest {
+ val taskId = 1
+ val expectedThumbnailData = createThumbnailData()
+ tasksRepository.seedThumbnailData(mapOf(taskId to expectedThumbnailData))
+ val expectedIconData = mock<Drawable>()
+ tasksRepository.seedIconData(taskId, "Task $taskId", "", expectedIconData)
+ tasksRepository.seedTasks(tasks)
+ tasksRepository.setVisibleTasks(setOf(taskId))
+ recentsViewData.runningTaskIds.value = setOf(taskId)
+ recentsViewData.runningTaskShowScreenshot.value = true
+ systemUnderTest.bind(taskId)
- assertThat(systemUnderTest.uiState.first())
- .isEqualTo(
- SnapshotSplash(
- Snapshot(
- backgroundColor = Color.rgb(1, 1, 1),
- bitmap = expectedThumbnailData.thumbnail!!,
- thumbnailRotation = Surface.ROTATION_0,
- ),
- expectedIconData,
+ assertThat(systemUnderTest.uiState.first())
+ .isEqualTo(
+ SnapshotSplash(
+ Snapshot(
+ backgroundColor = Color.rgb(1, 1, 1),
+ bitmap = expectedThumbnailData.thumbnail!!,
+ thumbnailRotation = Surface.ROTATION_0,
+ ),
+ expectedIconData,
+ )
)
- )
- }
+ }
@Test
- fun setRecentsFullscreenProgress_thenCornerRadiusProgressIsPassedThrough() = runTest {
- recentsViewData.fullscreenProgress.value = 0.5f
+ fun setRecentsFullscreenProgress_thenCornerRadiusProgressIsPassedThrough() =
+ testScope.runTest {
+ recentsViewData.fullscreenProgress.value = 0.5f
- assertThat(systemUnderTest.cornerRadiusProgress.first()).isEqualTo(0.5f)
+ assertThat(systemUnderTest.cornerRadiusProgress.first()).isEqualTo(0.5f)
- recentsViewData.fullscreenProgress.value = 0.6f
+ recentsViewData.fullscreenProgress.value = 0.6f
- assertThat(systemUnderTest.cornerRadiusProgress.first()).isEqualTo(0.6f)
- }
+ assertThat(systemUnderTest.cornerRadiusProgress.first()).isEqualTo(0.6f)
+ }
@Test
- fun setRecentsFullscreenProgress_thenCornerRadiusProgressIsConstantForDesktop() = runTest {
- taskViewType = TaskViewType.DESKTOP
- recentsViewData.fullscreenProgress.value = 0.5f
+ fun setRecentsFullscreenProgress_thenCornerRadiusProgressIsConstantForDesktop() =
+ testScope.runTest {
+ taskViewType = TaskViewType.DESKTOP
+ recentsViewData.fullscreenProgress.value = 0.5f
- assertThat(systemUnderTest.cornerRadiusProgress.first()).isEqualTo(1f)
+ assertThat(systemUnderTest.cornerRadiusProgress.first()).isEqualTo(1f)
- recentsViewData.fullscreenProgress.value = 0.6f
+ recentsViewData.fullscreenProgress.value = 0.6f
- assertThat(systemUnderTest.cornerRadiusProgress.first()).isEqualTo(1f)
- }
+ assertThat(systemUnderTest.cornerRadiusProgress.first()).isEqualTo(1f)
+ }
@Test
- fun setAncestorScales_thenScaleIsCalculated() = runTest {
- recentsViewData.scale.value = 0.5f
- taskViewData.scale.value = 0.6f
+ fun setAncestorScales_thenScaleIsCalculated() =
+ testScope.runTest {
+ recentsViewData.scale.value = 0.5f
+ taskViewData.scale.value = 0.6f
- assertThat(systemUnderTest.inheritedScale.first()).isEqualTo(0.3f)
- }
+ assertThat(systemUnderTest.inheritedScale.first()).isEqualTo(0.3f)
+ }
@Test
fun bindRunningTaskThenStoppedTaskWithoutThumbnail_thenStateChangesToBackgroundOnly() =
- runTest {
+ testScope.runTest {
val runningTaskId = 1
val stoppedTaskId = 2
tasksRepository.seedTasks(tasks)
@@ -161,125 +174,138 @@
}
@Test
- fun bindStoppedTaskWithoutThumbnail_thenStateIs_BackgroundOnly_withAlphaRemoved() = runTest {
- val stoppedTaskId = 2
- tasksRepository.seedTasks(tasks)
- tasksRepository.setVisibleTasks(setOf(stoppedTaskId))
+ fun bindStoppedTaskWithoutThumbnail_thenStateIs_BackgroundOnly_withAlphaRemoved() =
+ testScope.runTest {
+ val stoppedTaskId = 2
+ tasksRepository.seedTasks(tasks)
+ tasksRepository.setVisibleTasks(setOf(stoppedTaskId))
- systemUnderTest.bind(stoppedTaskId)
- assertThat(systemUnderTest.uiState.first())
- .isEqualTo(BackgroundOnly(backgroundColor = Color.rgb(2, 2, 2)))
- }
+ systemUnderTest.bind(stoppedTaskId)
+ assertThat(systemUnderTest.uiState.first())
+ .isEqualTo(BackgroundOnly(backgroundColor = Color.rgb(2, 2, 2)))
+ }
@Test
- fun bindLockedTaskWithThumbnail_thenStateIs_BackgroundOnly() = runTest {
- val taskId = 2
- tasksRepository.seedThumbnailData(mapOf(taskId to createThumbnailData()))
- tasks[taskId].isLocked = true
- tasksRepository.seedTasks(tasks)
- tasksRepository.setVisibleTasks(setOf(taskId))
+ fun bindLockedTaskWithThumbnail_thenStateIs_BackgroundOnly() =
+ testScope.runTest {
+ val taskId = 2
+ tasksRepository.seedThumbnailData(mapOf(taskId to createThumbnailData()))
+ tasks[taskId].isLocked = true
+ tasksRepository.seedTasks(tasks)
+ tasksRepository.setVisibleTasks(setOf(taskId))
- systemUnderTest.bind(taskId)
- assertThat(systemUnderTest.uiState.first())
- .isEqualTo(BackgroundOnly(backgroundColor = Color.rgb(2, 2, 2)))
- }
+ systemUnderTest.bind(taskId)
+ assertThat(systemUnderTest.uiState.first())
+ .isEqualTo(BackgroundOnly(backgroundColor = Color.rgb(2, 2, 2)))
+ }
@Test
- fun bindStoppedTaskWithThumbnail_thenStateIs_SnapshotSplash_withAlphaRemoved() = runTest {
- val taskId = 2
- val expectedThumbnailData = createThumbnailData(rotation = Surface.ROTATION_270)
- tasksRepository.seedThumbnailData(mapOf(taskId to expectedThumbnailData))
- val expectedIconData = mock<Drawable>()
- tasksRepository.seedIconData(taskId, "Task $taskId", "", expectedIconData)
- tasksRepository.seedTasks(tasks)
- tasksRepository.setVisibleTasks(setOf(taskId))
+ fun bindStoppedTaskWithThumbnail_thenStateIs_SnapshotSplash_withAlphaRemoved() =
+ testScope.runTest {
+ val taskId = 2
+ val expectedThumbnailData = createThumbnailData(rotation = Surface.ROTATION_270)
+ tasksRepository.seedThumbnailData(mapOf(taskId to expectedThumbnailData))
+ val expectedIconData = mock<Drawable>()
+ tasksRepository.seedIconData(taskId, "Task $taskId", "", expectedIconData)
+ tasksRepository.seedTasks(tasks)
+ tasksRepository.setVisibleTasks(setOf(taskId))
- systemUnderTest.bind(taskId)
- assertThat(systemUnderTest.uiState.first())
- .isEqualTo(
- SnapshotSplash(
- Snapshot(
- backgroundColor = Color.rgb(2, 2, 2),
- bitmap = expectedThumbnailData.thumbnail!!,
- thumbnailRotation = Surface.ROTATION_270,
- ),
- expectedIconData,
+ systemUnderTest.bind(taskId)
+ assertThat(systemUnderTest.uiState.first())
+ .isEqualTo(
+ SnapshotSplash(
+ Snapshot(
+ backgroundColor = Color.rgb(2, 2, 2),
+ bitmap = expectedThumbnailData.thumbnail!!,
+ thumbnailRotation = Surface.ROTATION_270,
+ ),
+ expectedIconData,
+ )
)
- )
- }
+ }
@Test
- fun bindNonVisibleStoppedTask_whenMadeVisible_thenStateIsSnapshotSplash() = runTest {
- val taskId = 2
- val expectedThumbnailData = createThumbnailData()
- tasksRepository.seedThumbnailData(mapOf(taskId to expectedThumbnailData))
- val expectedIconData = mock<Drawable>()
- tasksRepository.seedIconData(taskId, "Task $taskId", "", expectedIconData)
- tasksRepository.seedTasks(tasks)
+ fun bindNonVisibleStoppedTask_whenMadeVisible_thenStateIsSnapshotSplash() =
+ testScope.runTest {
+ val taskId = 2
+ val expectedThumbnailData = createThumbnailData()
+ tasksRepository.seedThumbnailData(mapOf(taskId to expectedThumbnailData))
+ val expectedIconData = mock<Drawable>()
+ tasksRepository.seedIconData(taskId, "Task $taskId", "", expectedIconData)
+ tasksRepository.seedTasks(tasks)
- systemUnderTest.bind(taskId)
- assertThat(systemUnderTest.uiState.first()).isEqualTo(Uninitialized)
+ systemUnderTest.bind(taskId)
+ assertThat(systemUnderTest.uiState.first()).isEqualTo(Uninitialized)
- tasksRepository.setVisibleTasks(setOf(taskId))
- assertThat(systemUnderTest.uiState.first())
- .isEqualTo(
- SnapshotSplash(
- Snapshot(
- backgroundColor = Color.rgb(2, 2, 2),
- bitmap = expectedThumbnailData.thumbnail!!,
- thumbnailRotation = Surface.ROTATION_0,
- ),
- expectedIconData,
+ tasksRepository.setVisibleTasks(setOf(taskId))
+ assertThat(systemUnderTest.uiState.first())
+ .isEqualTo(
+ SnapshotSplash(
+ Snapshot(
+ backgroundColor = Color.rgb(2, 2, 2),
+ bitmap = expectedThumbnailData.thumbnail!!,
+ thumbnailRotation = Surface.ROTATION_0,
+ ),
+ expectedIconData,
+ )
)
- )
- }
+ }
@Test
- fun getSnapshotMatrix_MissingThumbnail() = runTest {
- val taskId = 2
- val isRtl = true
+ fun getSnapshotMatrix_MissingThumbnail() =
+ testScope.runTest {
+ val taskId = 2
+ val isRtl = true
- whenever(mGetThumbnailPositionUseCase.run(taskId, CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
- .thenReturn(MissingThumbnail)
+ whenever(mGetThumbnailPositionUseCase.run(taskId, CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
+ .thenReturn(MissingThumbnail)
- systemUnderTest.bind(taskId)
- assertThat(systemUnderTest.getThumbnailPositionState(CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
- .isEqualTo(Matrix.IDENTITY_MATRIX)
- }
+ systemUnderTest.bind(taskId)
+ assertThat(
+ systemUnderTest.getThumbnailPositionState(CANVAS_WIDTH, CANVAS_HEIGHT, isRtl)
+ )
+ .isEqualTo(Matrix.IDENTITY_MATRIX)
+ }
@Test
- fun getSnapshotMatrix_MatrixScaling() = runTest {
- val taskId = 2
- val isRtl = true
+ fun getSnapshotMatrix_MatrixScaling() =
+ testScope.runTest {
+ val taskId = 2
+ val isRtl = true
- whenever(mGetThumbnailPositionUseCase.run(taskId, CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
- .thenReturn(MatrixScaling(MATRIX, isRotated = false))
+ whenever(mGetThumbnailPositionUseCase.run(taskId, CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
+ .thenReturn(MatrixScaling(MATRIX, isRotated = false))
- systemUnderTest.bind(taskId)
- assertThat(systemUnderTest.getThumbnailPositionState(CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
- .isEqualTo(MATRIX)
- }
+ systemUnderTest.bind(taskId)
+ assertThat(
+ systemUnderTest.getThumbnailPositionState(CANVAS_WIDTH, CANVAS_HEIGHT, isRtl)
+ )
+ .isEqualTo(MATRIX)
+ }
@Test
- fun getForegroundScrimDimProgress_returnsForegroundMaxScrim() = runTest {
- recentsViewData.tintAmount.value = 0.32f
- taskContainerData.taskMenuOpenProgress.value = 0f
- assertThat(systemUnderTest.dimProgress.first()).isEqualTo(0.32f)
- }
+ fun getForegroundScrimDimProgress_returnsForegroundMaxScrim() =
+ testScope.runTest {
+ recentsViewData.tintAmount.value = 0.32f
+ taskContainerData.taskMenuOpenProgress.value = 0f
+ assertThat(systemUnderTest.dimProgress.first()).isEqualTo(0.32f)
+ }
@Test
- fun getTaskMenuScrimDimProgress_returnsTaskMenuScrim() = runTest {
- recentsViewData.tintAmount.value = 0f
- taskContainerData.taskMenuOpenProgress.value = 1f
- assertThat(systemUnderTest.dimProgress.first()).isEqualTo(0.4f)
- }
+ fun getTaskMenuScrimDimProgress_returnsTaskMenuScrim() =
+ testScope.runTest {
+ recentsViewData.tintAmount.value = 0f
+ taskContainerData.taskMenuOpenProgress.value = 1f
+ assertThat(systemUnderTest.dimProgress.first()).isEqualTo(0.4f)
+ }
@Test
- fun getForegroundScrimDimProgress_returnsNoScrim() = runTest {
- recentsViewData.tintAmount.value = 0f
- taskContainerData.taskMenuOpenProgress.value = 0f
- assertThat(systemUnderTest.dimProgress.first()).isEqualTo(0f)
- }
+ fun getForegroundScrimDimProgress_returnsNoScrim() =
+ testScope.runTest {
+ recentsViewData.tintAmount.value = 0f
+ taskContainerData.taskMenuOpenProgress.value = 0f
+ assertThat(systemUnderTest.dimProgress.first()).isEqualTo(0f)
+ }
private fun createTaskWithId(taskId: Int) =
Task(Task.TaskKey(taskId, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/AppPairsControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/AppPairsControllerTest.kt
index 541a48d..ee70e0a 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/AppPairsControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/AppPairsControllerTest.kt
@@ -202,7 +202,7 @@
// Trigger app pair launch, capture and run callback from findLastActiveTasksAndRunCallback
spyAppPairsController.handleAppPairLaunchInApp(
mockAppPairIcon,
- listOf(mockItemInfo1, mockItemInfo2)
+ listOf(mockItemInfo1, mockItemInfo2),
)
verify(splitSelectStateController)
.findLastActiveTasksAndRunCallback(any(), any(), callbackCaptor.capture())
@@ -226,7 +226,7 @@
// Trigger app pair launch, capture and run callback from findLastActiveTasksAndRunCallback
spyAppPairsController.handleAppPairLaunchInApp(
mockAppPairIcon,
- listOf(mockItemInfo1, mockItemInfo2)
+ listOf(mockItemInfo1, mockItemInfo2),
)
verify(splitSelectStateController)
.findLastActiveTasksAndRunCallback(any(), any(), callbackCaptor.capture())
@@ -250,7 +250,7 @@
// Trigger app pair launch, capture and run callback from findLastActiveTasksAndRunCallback
spyAppPairsController.handleAppPairLaunchInApp(
mockAppPairIcon,
- listOf(mockItemInfo1, mockItemInfo2)
+ listOf(mockItemInfo1, mockItemInfo2),
)
verify(splitSelectStateController)
.findLastActiveTasksAndRunCallback(any(), any(), callbackCaptor.capture())
@@ -274,7 +274,7 @@
// Trigger app pair launch, capture and run callback from findLastActiveTasksAndRunCallback
spyAppPairsController.handleAppPairLaunchInApp(
mockAppPairIcon,
- listOf(mockItemInfo1, mockItemInfo2)
+ listOf(mockItemInfo1, mockItemInfo2),
)
verify(splitSelectStateController)
.findLastActiveTasksAndRunCallback(any(), any(), callbackCaptor.capture())
@@ -298,7 +298,7 @@
// Trigger app pair launch, capture and run callback from findLastActiveTasksAndRunCallback
spyAppPairsController.handleAppPairLaunchInApp(
mockAppPairIcon,
- listOf(mockItemInfo1, mockItemInfo2)
+ listOf(mockItemInfo1, mockItemInfo2),
)
verify(splitSelectStateController)
.findLastActiveTasksAndRunCallback(any(), any(), callbackCaptor.capture())
@@ -322,7 +322,7 @@
// Trigger app pair launch, capture and run callback from findLastActiveTasksAndRunCallback
spyAppPairsController.handleAppPairLaunchInApp(
mockAppPairIcon,
- listOf(mockItemInfo1, mockItemInfo2)
+ listOf(mockItemInfo1, mockItemInfo2),
)
verify(splitSelectStateController)
.findLastActiveTasksAndRunCallback(any(), any(), callbackCaptor.capture())
@@ -341,12 +341,16 @@
whenever(mockTaskKey1.getId()).thenReturn(1)
whenever(mockTaskKey2.getId()).thenReturn(2)
// ... with app 1 already on screen
- whenever(mockCachedTaskInfo.taskId).thenReturn(1)
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ whenever(mockCachedTaskInfo.topGroupedTaskContainsTask(eq(1))).thenReturn(true)
+ } else {
+ whenever(mockCachedTaskInfo.taskId).thenReturn(1)
+ }
// Trigger app pair launch, capture and run callback from findLastActiveTasksAndRunCallback
spyAppPairsController.handleAppPairLaunchInApp(
mockAppPairIcon,
- listOf(mockItemInfo1, mockItemInfo2)
+ listOf(mockItemInfo1, mockItemInfo2),
)
verify(splitSelectStateController)
.findLastActiveTasksAndRunCallback(any(), any(), callbackCaptor.capture())
@@ -365,12 +369,16 @@
whenever(mockTaskKey1.getId()).thenReturn(1)
whenever(mockTaskKey2.getId()).thenReturn(2)
// ... with app 2 already on screen
- whenever(mockCachedTaskInfo.taskId).thenReturn(2)
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ whenever(mockCachedTaskInfo.topGroupedTaskContainsTask(eq(2))).thenReturn(true)
+ } else {
+ whenever(mockCachedTaskInfo.taskId).thenReturn(2)
+ }
// Trigger app pair launch, capture and run callback from findLastActiveTasksAndRunCallback
spyAppPairsController.handleAppPairLaunchInApp(
mockAppPairIcon,
- listOf(mockItemInfo1, mockItemInfo2)
+ listOf(mockItemInfo1, mockItemInfo2),
)
verify(splitSelectStateController)
.findLastActiveTasksAndRunCallback(any(), any(), callbackCaptor.capture())
@@ -389,12 +397,16 @@
whenever(mockTaskKey1.getId()).thenReturn(1)
whenever(mockTaskKey2.getId()).thenReturn(2)
// ... with app 3 already on screen
- whenever(mockCachedTaskInfo.taskId).thenReturn(3)
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ whenever(mockCachedTaskInfo.topGroupedTaskContainsTask(eq(3))).thenReturn(true)
+ } else {
+ whenever(mockCachedTaskInfo.taskId).thenReturn(3)
+ }
// Trigger app pair launch, capture and run callback from findLastActiveTasksAndRunCallback
spyAppPairsController.handleAppPairLaunchInApp(
mockAppPairIcon,
- listOf(mockItemInfo1, mockItemInfo2)
+ listOf(mockItemInfo1, mockItemInfo2),
)
verify(splitSelectStateController)
.findLastActiveTasksAndRunCallback(any(), any(), callbackCaptor.capture())
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
index 59413d3..066ddc0 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
@@ -32,6 +32,7 @@
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.TaskItemInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.quickstep.RecentsModel
import com.android.quickstep.RecentsModel.RecentTasksChangedListener
import com.android.quickstep.TaskIconCache
@@ -78,7 +79,9 @@
private var taskListChangeId: Int = 1
private lateinit var recentAppsController: TaskbarRecentAppsController
- private lateinit var userHandle: UserHandle
+ private lateinit var myUserHandle: UserHandle
+ private val USER_HANDLE_1 = UserHandle.of(1)
+ private val USER_HANDLE_2 = UserHandle.of(2)
private var canShowRunningAndRecentAppsAtInit = true
private var recentTasksChangedListener: RecentTasksChangedListener? = null
@@ -86,7 +89,7 @@
@Before
fun setUp() {
super.setup()
- userHandle = Process.myUserHandle()
+ myUserHandle = Process.myUserHandle()
// Set desktop mode supported
whenever(mockContext.getResources()).thenReturn(mockResources)
@@ -149,6 +152,84 @@
}
@Test
+ fun getDesktopItemState_nullItemInfo_returnsNotRunning() {
+ setInDesktopMode(true)
+ assertThat(recentAppsController.getDesktopItemState(/* itemInfo= */ null))
+ .isEqualTo(RunningAppState.NOT_RUNNING)
+ }
+
+ @Test
+ fun getDesktopItemState_noItemPackage_returnsNotRunning() {
+ setInDesktopMode(true)
+ assertThat(recentAppsController.getDesktopItemState(ItemInfo()))
+ .isEqualTo(RunningAppState.NOT_RUNNING)
+ }
+
+ @Test
+ fun getDesktopItemState_noMatchingTasks_returnsNotRunning() {
+ setInDesktopMode(true)
+ val itemInfo = createItemInfo("package")
+ assertThat(recentAppsController.getDesktopItemState(itemInfo))
+ .isEqualTo(RunningAppState.NOT_RUNNING)
+ }
+
+ @Test
+ fun getDesktopItemState_matchingVisibleTask_returnsVisible() {
+ setInDesktopMode(true)
+ val visibleTask = createTask(id = 1, "visiblePackage", isVisible = true)
+ updateRecentTasks(runningTasks = listOf(visibleTask), recentTaskPackages = emptyList())
+ val itemInfo = createItemInfo("visiblePackage")
+
+ assertThat(recentAppsController.getDesktopItemState(itemInfo))
+ .isEqualTo(RunningAppState.RUNNING)
+ }
+
+ @Test
+ fun getDesktopItemState_matchingMinimizedTask_returnsMinimized() {
+ setInDesktopMode(true)
+ val minimizedTask = createTask(id = 1, "minimizedPackage", isVisible = false)
+ updateRecentTasks(runningTasks = listOf(minimizedTask), recentTaskPackages = emptyList())
+ val itemInfo = createItemInfo("minimizedPackage")
+
+ assertThat(recentAppsController.getDesktopItemState(itemInfo))
+ .isEqualTo(RunningAppState.MINIMIZED)
+ }
+
+ @Test
+ fun getDesktopItemState_matchingMinimizedAndRunningTask_returnsVisible() {
+ setInDesktopMode(true)
+ updateRecentTasks(
+ runningTasks =
+ listOf(
+ createTask(id = 1, "package", isVisible = false),
+ createTask(id = 2, "package", isVisible = true),
+ ),
+ recentTaskPackages = emptyList(),
+ )
+ val itemInfo = createItemInfo("package")
+
+ assertThat(recentAppsController.getDesktopItemState(itemInfo))
+ .isEqualTo(RunningAppState.RUNNING)
+ }
+
+ @Test
+ fun getDesktopItemState_noMatchingUserId_returnsNotRunning() {
+ setInDesktopMode(true)
+ updateRecentTasks(
+ runningTasks =
+ listOf(
+ createTask(id = 1, "package", isVisible = false, USER_HANDLE_1),
+ createTask(id = 2, "package", isVisible = true, USER_HANDLE_1),
+ ),
+ recentTaskPackages = emptyList(),
+ )
+ val itemInfo = createItemInfo("package", USER_HANDLE_2)
+
+ assertThat(recentAppsController.getDesktopItemState(itemInfo))
+ .isEqualTo(RunningAppState.NOT_RUNNING)
+ }
+
+ @Test
fun getRunningAppState_taskNotRunningOrMinimized_returnsNotRunning() {
setInDesktopMode(true)
updateRecentTasks(runningTasks = emptyList(), recentTaskPackages = emptyList())
@@ -814,7 +895,13 @@
private fun createTestAppInfo(
packageName: String = "testPackageName",
className: String = "testClassName",
- ) = AppInfo(ComponentName(packageName, className), className /* title */, userHandle, Intent())
+ ) =
+ AppInfo(
+ ComponentName(packageName, className),
+ className /* title */,
+ myUserHandle,
+ Intent(),
+ )
private fun createRecentTasksFromPackageNames(packageNames: List<String>): List<GroupTask> {
return packageNames.map { packageName ->
@@ -833,14 +920,19 @@
}
}
- private fun createTask(id: Int, packageName: String, isVisible: Boolean = true): Task {
+ private fun createTask(
+ id: Int,
+ packageName: String,
+ isVisible: Boolean = true,
+ localUserHandle: UserHandle? = null,
+ ): Task {
return Task(
Task.TaskKey(
id,
WINDOWING_MODE_FREEFORM,
Intent().apply { `package` = packageName },
ComponentName(packageName, "TestActivity"),
- userHandle.identifier,
+ localUserHandle?.identifier ?: myUserHandle.identifier,
0,
)
)
@@ -852,6 +944,16 @@
.thenReturn(inDesktopMode)
}
+ private fun createItemInfo(
+ packageName: String,
+ userHandle: UserHandle = myUserHandle,
+ ): ItemInfo {
+ val appInfo = AppInfo()
+ appInfo.intent = Intent().setComponent(ComponentName(packageName, "className"))
+ appInfo.user = userHandle
+ return WorkspaceItemInfo(appInfo)
+ }
+
private val GroupTask.packageNames: List<String>
get() = tasks.map { task -> task.key.packageName }
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index 44c23ba..6a7b6f8 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
@@ -26,6 +26,7 @@
import com.android.launcher3.tapl.LaunchedAppState;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.TestUtil;
import com.android.quickstep.views.RecentsView;
import org.junit.rules.RuleChain;
@@ -56,7 +57,7 @@
protected void assertTestActivityIsRunning(int activityNumber, String message) {
assertTrue(message, mDevice.wait(
Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity" + activityNumber)),
- DEFAULT_UI_TIMEOUT));
+ TestUtil.DEFAULT_UI_TIMEOUT));
}
protected LaunchedAppState getAndAssertLaunchedApp() {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
similarity index 73%
rename from quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
rename to quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index e981570..5b46dc8 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -15,7 +15,9 @@
*/
package com.android.quickstep;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.util.TestUtil.resolveSystemAppInfo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -25,10 +27,13 @@
import android.app.usage.UsageStatsManager;
import android.content.Intent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.BaseLauncherActivityTest;
import com.android.quickstep.views.DigitalWellBeingToast;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskContainer;
@@ -41,30 +46,31 @@
@LargeTest
@RunWith(AndroidJUnit4.class)
-public class TaplDigitalWellBeingToastTest extends AbstractQuickStepTest {
- private static final String CALCULATOR_PACKAGE =
- resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
+public class DigitalWellBeingToastTest extends BaseLauncherActivityTest<QuickstepLauncher> {
+
+ public final String calculatorPackage =
+ resolveSystemAppInfo(Intent.CATEGORY_APP_CALCULATOR).packageName;
@Test
- public void testToast() throws Exception {
- startAppFast(CALCULATOR_PACKAGE);
+ public void testToast() {
+ startAppFast(calculatorPackage);
final UsageStatsManager usageStatsManager =
- mTargetContext.getSystemService(UsageStatsManager.class);
+ targetContext().getSystemService(UsageStatsManager.class);
final int observerId = 0;
try {
- final String[] packages = new String[]{CALCULATOR_PACKAGE};
+ final String[] packages = new String[]{calculatorPackage};
// Set time limit for app.
runWithShellPermission(() ->
usageStatsManager.registerAppUsageLimitObserver(observerId, packages,
Duration.ofSeconds(600), Duration.ofSeconds(300),
- PendingIntent.getActivity(mTargetContext, -1, new Intent()
- .setPackage(mTargetContext.getPackageName()),
+ PendingIntent.getActivity(targetContext(), -1, new Intent()
+ .setPackage(targetContext().getPackageName()),
PendingIntent.FLAG_MUTABLE)));
- mLauncher.goHome();
+ loadLauncherSync();
final DigitalWellBeingToast toast = getToast();
waitForLauncherCondition("Toast is not visible", launcher -> toast.getHasLimit());
@@ -74,7 +80,7 @@
runWithShellPermission(
() -> usageStatsManager.unregisterAppUsageLimitObserver(observerId));
- mLauncher.goHome();
+ goToState(LauncherState.NORMAL);
assertFalse("Toast is visible", getToast().getHasLimit());
} finally {
runWithShellPermission(
@@ -83,12 +89,12 @@
}
private DigitalWellBeingToast getToast() {
- mLauncher.getWorkspace().switchToOverview();
+ goToState(LauncherState.OVERVIEW);
final TaskView task = getOnceNotNull("No latest task", launcher -> getLatestTask(launcher));
return getFromLauncher(launcher -> {
TaskContainer taskContainer = task.getTaskContainers().get(0);
- assertTrue("Latest task is not Calculator", CALCULATOR_PACKAGE.equals(
+ assertTrue("Latest task is not Calculator", calculatorPackage.equals(
taskContainer.getTask().getTopComponent().getPackageName()));
return taskContainer.getDigitalWellBeingToast();
});
@@ -105,6 +111,5 @@
} finally {
getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
}
-
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 1f11c14..aa105f9 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -22,9 +22,7 @@
import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
import static com.android.launcher3.tapl.TestHelpers.getHomeIntentInPackage;
import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess;
-import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_ACTIVITY_TIMEOUT;
import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_BROADCAST_TIMEOUT_SECS;
-import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT;
import static com.android.launcher3.ui.AbstractLauncherUiTest.resolveSystemApp;
import static com.android.launcher3.ui.AbstractLauncherUiTest.startAppFast;
import static com.android.launcher3.ui.AbstractLauncherUiTest.startTestActivity;
@@ -56,6 +54,7 @@
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ExtendedLongPressTimeoutRule;
import com.android.launcher3.util.rule.FailureWatcher;
@@ -214,7 +213,7 @@
}
result[0] = f.apply(activity);
return true;
- }).get(), DEFAULT_UI_TIMEOUT, mLauncher);
+ }).get(), mLauncher);
return (T) result[0];
}
@@ -244,7 +243,7 @@
Wait.atMost("Recents activity didn't stop",
() -> getFromRecents(recents -> !recents.isStarted()),
- DEFAULT_UI_TIMEOUT, mLauncher);
+ mLauncher);
}
@Test
@@ -254,7 +253,8 @@
startTestActivity(2);
waitForRecentsActivityStop();
Wait.atMost("Expected three apps in the task list",
- () -> mLauncher.getRecentTasks().size() >= 3, DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
+ () -> mLauncher.getRecentTasks().size() >= 3,
+ mLauncher);
checkTestLauncher();
BaseOverview overview = mLauncher.getLaunchedAppState().switchToOverview();
@@ -282,7 +282,7 @@
assertNotNull("OverviewTask.open returned null", task.open());
assertTrue("Test activity didn't open from Overview", TestHelpers.wait(Until.hasObject(
By.pkg(getAppPackageName()).text("TestActivity2")),
- DEFAULT_UI_TIMEOUT));
+ TestUtil.DEFAULT_UI_TIMEOUT));
// Test dismissing a task.
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 4459ed6..77f4c05 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -57,8 +57,6 @@
static final String TAG = "QuickStepOnOffRule";
- public static final int WAIT_TIME_MS = 10000;
-
public enum Mode {
THREE_BUTTON, ZERO_BUTTON, ALL
}
@@ -179,12 +177,13 @@
}
Wait.atMost("Couldn't switch to " + overlayPackage,
- () -> launcher.getNavigationModel() == expectedMode, WAIT_TIME_MS, launcher);
+ () -> launcher.getNavigationModel() == expectedMode,
+ launcher);
Wait.atMost(() -> "Switching nav mode: "
+ launcher.getNavigationModeMismatchError(false),
() -> launcher.getNavigationModeMismatchError(false) == null,
- WAIT_TIME_MS, launcher);
+ launcher);
AbstractLauncherUiTest.checkDetectedLeaks(launcher, false);
return true;
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplStartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/TaplStartLauncherViaGestureTests.java
index a8f39af..2fb08dd 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplStartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplStartLauncherViaGestureTests.java
@@ -16,6 +16,8 @@
package com.android.quickstep;
+import android.util.Log;
+
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -29,6 +31,8 @@
@RunWith(AndroidJUnit4.class)
public class TaplStartLauncherViaGestureTests extends AbstractQuickStepTest {
+ public static final String TAG = "TaplStartLauncherViaGestureTests";
+
static final int STRESS_REPEAT_COUNT = 10;
private enum TestCase {
@@ -69,7 +73,9 @@
}
private void runTest(TestCase testCase) {
+ long testStartTime = System.currentTimeMillis();
for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
+ long loopStartTime = System.currentTimeMillis();
// Destroy Launcher activity.
closeLauncherActivity();
@@ -84,7 +90,10 @@
default:
throw new IllegalStateException("Cannot run test case: " + testCase);
}
+ Log.d(TAG, "Loop " + (i + 1) + " runtime="
+ + (System.currentTimeMillis() - loopStartTime) + "ms");
}
+ Log.d(TAG, "Test runtime=" + (System.currentTimeMillis() - testStartTime) + "ms");
switch (testCase) {
case TO_OVERVIEW:
closeLauncherActivity();
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java b/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java
index 43ebb17..3c4f1d9 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java
@@ -49,6 +49,7 @@
DISMISS(0),
LAUNCH_LAST_APP(0),
LAUNCH_SELECTED_APP(1),
+ DISMISS_WHEN_GOING_HOME(1),
LAUNCH_OVERVIEW(KeyboardQuickSwitchController.MAX_TASKS - 1);
private final int mNumAdditionalRunningTasks;
@@ -156,6 +157,11 @@
mLauncher.goHome().showQuickSwitchView().launchFocusedAppTask(CALCULATOR_APP_PACKAGE);
}
+ @Test
+ public void testDismissedWhenGoingHome() {
+ runTest(TestSurface.LAUNCHED_APP, TestCase.DISMISS_WHEN_GOING_HOME);
+ }
+
private void runTest(@NonNull TestSurface testSurface, @NonNull TestCase testCase) {
for (int i = 0; i < testCase.mNumAdditionalRunningTasks; i++) {
startTestActivity(3 + i);
@@ -197,6 +203,9 @@
}
kqs.launchFocusedAppTask(CALCULATOR_APP_PACKAGE);
break;
+ case DISMISS_WHEN_GOING_HOME:
+ kqs.dismissByGoingHome();
+ break;
case LAUNCH_OVERVIEW:
kqs.moveFocusBackward();
if (!testSurface.mInitialFocusAtZero) {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt b/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
index 120a89b..f58c84e 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
@@ -26,6 +26,7 @@
import com.android.launcher3.ui.AbstractLauncherUiTest
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape
import com.android.launcher3.uioverrides.QuickstepLauncher
+import com.android.launcher3.util.TestUtil
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Before
import org.junit.Test
@@ -172,7 +173,7 @@
.that(
mDevice.wait(
Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity$index")),
- DEFAULT_UI_TIMEOUT,
+ TestUtil.DEFAULT_UI_TIMEOUT,
)
)
.isTrue()
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 5ff2af7..f1fe2d2 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -47,6 +47,7 @@
import com.android.launcher3.tapl.SelectModeButtons;
import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.launcher3.util.rule.TestStabilityRule;
@@ -145,7 +146,7 @@
assertNotNull("OverviewTask.open returned null", task.open());
assertTrue("Test activity didn't open from Overview", mDevice.wait(Until.hasObject(
By.pkg(getAppPackageName()).text("TestActivity2")),
- DEFAULT_UI_TIMEOUT));
+ TestUtil.DEFAULT_UI_TIMEOUT));
executeOnLauncher(launcher -> assertTrue(
"Launcher activity is the top activity; expecting another activity to be the top "
+ "one",
@@ -448,7 +449,7 @@
mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()).text(
mLauncher.isGridOnlyOverviewEnabled() ? "TestActivity12"
: "TestActivity13")),
- DEFAULT_UI_TIMEOUT));
+ TestUtil.DEFAULT_UI_TIMEOUT));
// Scroll the task offscreen as it is now first
overview = mLauncher.goHome().switchToOverview();
@@ -563,7 +564,7 @@
mLauncher.getDevice().setOrientationLeft();
startTestActivity(7);
Wait.atMost("Device should not be in natural orientation",
- () -> !mDevice.isNaturalOrientation(), DEFAULT_UI_TIMEOUT, mLauncher);
+ () -> !mDevice.isNaturalOrientation(), mLauncher);
mLauncher.goHome();
} finally {
mLauncher.setExpectedRotationCheckEnabled(true);
diff --git a/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchTransitionManagerTest.kt b/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchTransitionManagerTest.kt
new file mode 100644
index 0000000..26189df
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchTransitionManagerTest.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.desktop
+
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.content.Context
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.window.TransitionFilter
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.quickstep.SystemUiProxy
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DesktopAppLaunchTransitionManagerTest {
+
+ @get:Rule val mSetFlagsRule = SetFlagsRule()
+
+ private val mockitoSession =
+ mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .mockStatic(DesktopModeStatus::class.java)
+ .startMocking()
+
+ private val context = mock<Context>()
+ private val systemUiProxy = mock<SystemUiProxy>()
+ private lateinit var transitionManager: DesktopAppLaunchTransitionManager
+
+ @Before
+ fun setUp() {
+ whenever(context.resources).thenReturn(mock())
+ whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
+ transitionManager = DesktopAppLaunchTransitionManager(context, systemUiProxy)
+ }
+
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ fun registerTransitions_appLaunchFlagEnabled_registersTransition() {
+ transitionManager.registerTransitions()
+
+ verify(systemUiProxy, times(1)).registerRemoteTransition(any(), any())
+ }
+
+ @Test
+ @DisableFlags(FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ fun registerTransitions_appLaunchFlagDisabled_doesntRegisterTransition() {
+ transitionManager.registerTransitions()
+
+ verify(systemUiProxy, times(0)).registerRemoteTransition(any(), any())
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ fun registerTransitions_usesCorrectFilter() {
+ transitionManager.registerTransitions()
+ val filterArgumentCaptor = argumentCaptor<TransitionFilter>()
+
+ verify(systemUiProxy, times(1))
+ .registerRemoteTransition(any(), filterArgumentCaptor.capture())
+
+ assertThat(filterArgumentCaptor.lastValue).isNotNull()
+ assertThat(filterArgumentCaptor.lastValue.mTypeSet)
+ .isEqualTo(intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT))
+ assertThat(filterArgumentCaptor.lastValue.mRequirements).hasLength(1)
+ val launchRequirement = filterArgumentCaptor.lastValue.mRequirements!![0]
+ assertThat(launchRequirement.mModes).isEqualTo(intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT))
+ assertThat(launchRequirement.mActivityType).isEqualTo(ACTIVITY_TYPE_STANDARD)
+ assertThat(launchRequirement.mWindowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+}
diff --git a/res/drawable/bg_widgets_header_states_two_pane.xml b/res/drawable/bg_widgets_header_states_two_pane.xml
index 5f4b8c6..1ec41a9 100644
--- a/res/drawable/bg_widgets_header_states_two_pane.xml
+++ b/res/drawable/bg_widgets_header_states_two_pane.xml
@@ -14,18 +14,16 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item android:state_expanded="true">
- <shape android:shape="rectangle">
- <solid android:color="?attr/widgetPickerHeaderBackgroundColor" />
- <corners android:radius="@dimen/widget_list_top_bottom_corner_radius" />
- </shape>
+ <item android:state_expanded="true" android:state_focused="false">
+ <ripple android:color="@color/accent_ripple_color">
+ <item android:drawable="@drawable/bg_widgets_header_two_pane_expanded_unfocused" />
+ </ripple>
</item>
-
- <item android:state_expanded="false">
- <shape android:shape="rectangle">
- <solid android:color="@android:color/transparent" />
- <corners android:radius="@dimen/widget_list_top_bottom_corner_radius" />
- </shape>
+ <item android:state_expanded="false" android:state_focused="false">
+ <ripple android:color="@color/accent_ripple_color">
+ <item android:drawable="@drawable/bg_widgets_header_two_pane_unexpanded_unfocused" />
+ </ripple>
</item>
+ <item android:drawable="@drawable/bg_widgets_header_two_pane_expanded_focused" android:state_expanded="true" android:state_focused="true" />
+ <item android:drawable="@drawable/bg_widgets_header_two_pane_unexpanded_focused" android:state_expanded="false" android:state_focused="true" />
</selector>
diff --git a/res/drawable/bg_widgets_header_two_pane.xml b/res/drawable/bg_widgets_header_two_pane.xml
index ca3feef..e237002 100644
--- a/res/drawable/bg_widgets_header_two_pane.xml
+++ b/res/drawable/bg_widgets_header_two_pane.xml
@@ -14,13 +14,10 @@
limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetTop="@dimen/widget_list_entry_spacing" >
- <ripple
- android:color="@color/accent_ripple_color"
- android:paddingTop="@dimen/widget_list_header_view_vertical_padding"
- android:paddingBottom="@dimen/widget_list_header_view_vertical_padding" >
- <item android:id="@android:id/mask"
- android:drawable="@drawable/bg_widgets_header_states_two_pane" />
+ android:insetTop="@dimen/widget_list_entry_spacing">
+ <layer-list
+ android:paddingBottom="@dimen/widget_list_header_view_vertical_padding"
+ android:paddingTop="@dimen/widget_list_header_view_vertical_padding">
<item android:drawable="@drawable/bg_widgets_header_states_two_pane" />
- </ripple>
-</inset>
+ </layer-list>
+</inset>
\ No newline at end of file
diff --git a/res/drawable/bg_widgets_header_two_pane_expanded_focused.xml b/res/drawable/bg_widgets_header_two_pane_expanded_focused.xml
new file mode 100644
index 0000000..0ee3d14
--- /dev/null
+++ b/res/drawable/bg_widgets_header_two_pane_expanded_focused.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Draw the focus ring -->
+ <item>
+ <shape>
+ <corners android:radius="@dimen/widget_focus_ring_corner_radius" />
+ <stroke
+ android:width="@dimen/widget_header_focus_ring_width"
+ android:color="?attr/widgetPickerTabBackgroundSelected" />
+ </shape>
+ </item>
+
+ <!-- Draw the background with padding to make it spaced within the focus ring. -->
+ <item
+ android:bottom="@dimen/widget_header_background_border"
+ android:end="@dimen/widget_header_background_border"
+ android:start="@dimen/widget_header_background_border"
+ android:top="@dimen/widget_header_background_border">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/widget_list_top_bottom_corner_radius" />
+ <solid android:color="?attr/widgetPickerHeaderBackgroundColor" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/bg_widgets_header_two_pane_expanded_unfocused.xml b/res/drawable/bg_widgets_header_two_pane_expanded_unfocused.xml
new file mode 100644
index 0000000..9028ebe
--- /dev/null
+++ b/res/drawable/bg_widgets_header_two_pane_expanded_unfocused.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ android:paddingBottom="@dimen/widget_list_header_view_vertical_padding"
+ android:paddingTop="@dimen/widget_list_header_view_vertical_padding">
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/widget_list_top_bottom_corner_radius" />
+ <solid android:color="?attr/widgetPickerHeaderBackgroundColor" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/bg_widgets_header_two_pane_unexpanded_focused.xml b/res/drawable/bg_widgets_header_two_pane_unexpanded_focused.xml
new file mode 100644
index 0000000..12dc907
--- /dev/null
+++ b/res/drawable/bg_widgets_header_two_pane_unexpanded_focused.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Draw the focus ring and a transparent background -->
+ <item>
+ <shape>
+ <corners android:radius="@dimen/widget_focus_ring_corner_radius" />
+ <solid android:color="@android:color/transparent" />
+ <stroke
+ android:width="@dimen/widget_header_focus_ring_width"
+ android:color="?attr/widgetPickerTabBackgroundSelected" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/bg_widgets_header_two_pane_unexpanded_unfocused.xml b/res/drawable/bg_widgets_header_two_pane_unexpanded_unfocused.xml
new file mode 100644
index 0000000..ba26f9f
--- /dev/null
+++ b/res/drawable/bg_widgets_header_two_pane_unexpanded_unfocused.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/widget_list_top_bottom_corner_radius" />
+ <solid android:color="@android:color/transparent" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index 622f0d6..7c57726 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -81,6 +81,7 @@
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="@drawable/widgets_surface_background"
+ android:clipToOutline="true"
android:orientation="vertical"
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:visibility="gone">
diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml
index 5427732..1ce1c55 100644
--- a/res/layout/widgets_full_sheet_recyclerview.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -64,6 +64,7 @@
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="@drawable/widgets_surface_background"
+ android:clipToOutline="true"
android:orientation="vertical"
android:visibility="gone">
<include layout="@layout/widget_recommendations" />
diff --git a/res/layout/widgets_list_expand_button.xml b/res/layout/widgets_list_expand_button.xml
index 17c19ac..ff2d777 100644
--- a/res/layout/widgets_list_expand_button.xml
+++ b/res/layout/widgets_list_expand_button.xml
@@ -15,6 +15,7 @@
-->
<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/widget_list_expand_button"
style="@style/Button.Rounded.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/res/layout/widgets_two_pane_sheet.xml b/res/layout/widgets_two_pane_sheet.xml
index 5dc1b47..cf090ad 100644
--- a/res/layout/widgets_two_pane_sheet.xml
+++ b/res/layout/widgets_two_pane_sheet.xml
@@ -133,6 +133,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/widgets_surface_background"
+ android:clipToOutline="true"
android:orientation="vertical"
android:visibility="gone">
<include layout="@layout/widget_recommendations" />
diff --git a/res/layout/work_mode_fab.xml b/res/layout/work_mode_fab.xml
index 46f2d8a..e2f0e09 100644
--- a/res/layout/work_mode_fab.xml
+++ b/res/layout/work_mode_fab.xml
@@ -17,6 +17,7 @@
android:id="@+id/work_mode_toggle"
android:layout_height="@dimen/work_fab_height"
android:layout_width="wrap_content"
+ android:elevation="@dimen/work_fab_elevation"
android:minHeight="@dimen/work_fab_height"
android:gravity="center_vertical"
android:background="@drawable/work_mode_fab_background"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index c956621..5181bb7 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Neem notas"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Voeg by"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Voeg <xliff:g id="WIDGET_NAME">%1$s</xliff:g>-legstuk by"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tik om legstukinstellings te verander"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Verander legstukinstellings"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Deursoek programme"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gedeaktiveer deur jou administrateur"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Laat toe dat tuisskerm gedraai word"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Wanneer foon gedraai word"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Kennisgewingkolle"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Aan"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Af"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Het dit"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Onderbreek werkprogramme"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Hervat"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Misluk: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privaat ruimte"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index cfb5585..a217bb6 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"የማስታወሻ አያያዝ"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"አክል"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"ምግብር <xliff:g id="WIDGET_NAME">%1$s</xliff:g>ን አክል"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"የምግብር ቅንብሮችን ለመለወጥ መታ ያድርጉ"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"የምግብር ቅንብሮችን ይለውጡ"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"መተግበሪያዎችን ፈልግ"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"በእርስዎ አስተዳዳሪ የተሰናከለ"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"የመነሻ ማያ ገፅ ማሽከርከርን ይፍቀዱ"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"ስልኩ ሲዞር"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"የማሳወቂያ ነጥቦች"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"አብራ"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"ጠፍቷል"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ገባኝ"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"የሥራ መተግበሪያዎችን ባሉበት አቁም"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"ካቆመበት ቀጥል"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"አጣራ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"አልተሳካም፦ <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"የግል ቦታ"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 6429b53..c08254a 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"تدوين الملاحظات"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"إضافة"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"إضافة التطبيق المصغّر \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\""</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"انقر لتغيير إعدادات الأداة"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"تغيير إعدادات الأداة"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"بحث في التطبيقات"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"أوقف المشرف هذه الميزة"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"السماح بتدوير الشاشة الرئيسية"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"عند تدوير الهاتف"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"نقاط الإشعارات"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"الإعداد مفعّل"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"غير مفعّل"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"حسنًا"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"إيقاف تطبيقات العمل مؤقتًا"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"إلغاء الإيقاف المؤقت"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"فلتر"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"تعذَّر <xliff:g id="WHAT">%1$s</xliff:g>."</string>
<string name="private_space_label" msgid="2359721649407947001">"مساحة خاصة"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index f1db033..ccfb10e 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"টোকা গ্ৰহণ কৰা"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"যোগ দিয়ক"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ৱিজেট যোগ দিয়ক"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ৱিজেটৰ ছেটিং সলনি কৰিবলৈ টিপক"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ৱিজেটৰ ছেটিং সলনি কৰক"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"এপ্সমূহ সন্ধান কৰক"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"আপোনাৰ প্ৰশাসকে অক্ষম কৰি ৰাখিছে"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"গৃহ স্ক্ৰীন ঘূৰোৱাৰ অনুমতি দিয়ক"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"ফ\'নটো যেতিয়া ঘূৰোৱা হয়"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"জাননী বিন্দু"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"অন কৰা আছে"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"অফ আছে"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"বুজি পালোঁ"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"কৰ্মস্থানৰ এপ্ পজ কৰক"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"আনপজ কৰক"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ফিল্টাৰ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"বিফল: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"প্ৰাইভেট স্পে\'চ"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index f2129cc..c85f271 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Qeydgötürmə"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Əlavə edin"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidcet əlavə edin"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Vidcet ayarlarını dəyişmək üçün toxunun"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Vidcet ayarlarını dəyişin"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Tətbiqləri axtarın"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Admininiz tərəfindən deaktiv edilib"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Əsas ekran çevrilsin"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon çevrilən zaman"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Bildiriş nöqtələri"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Aktiv"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Deaktiv"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Anladım"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"İş tətbiqlərini durdurun"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Davam etdirin"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtr"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Alınmadı: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Məxfi sahə"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index bdce3b7..e8b55b7 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Pravljenje beležaka"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Dodaj"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Dodajte vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Dodirnite da biste promenili podešavanja vidžeta"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Promenite podešavanja vidžeta"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pretražite aplikacije"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator je onemogućio"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Dozvoli rotaciju početnog ekrana"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon rotira"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Tačke za obaveštenja"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Uključeno"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Isključeno"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Važi"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pauziraj poslovne aplikacije"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Ponovo aktiviraj"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nije uspelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privatni prostor"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 8f264e2..6d714bc 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Стварэнне нататак"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Дадаць"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Дадаць віджэт \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\""</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Націсніце, каб змяніць налады віджэта"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Змяніць налады віджэта"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Пошук праграм"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Адключаная адміністратарам"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Дазволіць паварот галоўнага экрана"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Пры павароце тэлефона"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Значкі апавяшчэнняў"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Уключана"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Выкл."</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Зразумела"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Прыпыніць працоўныя праграмы"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Актываваць"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Фільтр"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Не ўдалося: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Прыватная прастора"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index efa941f..191ef03 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Водене на бележки"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Добавяне"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Добавяне на приспособлението „<xliff:g id="WIDGET_NAME">%1$s</xliff:g>“"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Докоснете, за да промените настройките на приспособлението"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Промяна на настройките на приспособлението"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Търсене в приложенията"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Деактивирано от администратора ви"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Разрешаване на завъртането на началния екран"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"При завъртане на телефона"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Точки за известия"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Вкл."</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Изкл."</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Разбрах"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Поставяне на пауза на служебните приложения"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Отмяна на паузата"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтър"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Неуспешно: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Частно пространство"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 0ccedd1..43834af 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"নোট নেওয়া"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"যোগ করুন"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> উইজেট যোগ করুন"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"উইজেট সেটিংস পরিবর্তন করতে ট্যাপ করুন"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"উইজেট সেটিংস পরিবর্তন করুন"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"অ্যাপ খুঁজুন"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"আপনার প্রশাসক দ্বারা অক্ষম করা হয়েছে"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"হোম স্ক্রিন রোটেট করার অনুমতি দিন"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"যখন ফোনটি ঘোরানো হয়"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"বিজ্ঞপ্তি ডট"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"চালু করা আছে"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"বন্ধ"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"বুঝেছি"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"অফিসের অ্যাপ পজ করুন"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"আনপজ করুন"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ফিল্টার"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"কাজটি করা যায়নি: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"ব্যক্তিগত স্পেস"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index a276431..0beeafe 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Pisanje bilješki"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Dodajte"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Dodavanje vidžeta <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Dodirnite da promijenite postavke vidžeta"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Promjena postavki vidžeta"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pretražite aplikacije"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio vaš administrator"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Dozvoli rotiranje početnog ekrana"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon zarotira"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Tačke za obavještenja"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Uključeno"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Isključeno"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Razumijem"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pauziraj poslovne aplikacije"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Ponovo pokreni"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrirajte"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nije uspjelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privatni prostor"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 8f7151e..94a7b41 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Presa de notes"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Afegeix"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Afegeix el widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toca per canviar la configuració del widget"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Canvia la configuració del widget"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Cerca aplicacions"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desactivada per l\'administrador"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Permet la rotació de la pantalla d\'inici"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"En girar el telèfon"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Punts de notificació"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Activats"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Desactivats"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Entesos"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Posa en pausa les aplicacions de treball"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Reactiva"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtra"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Espai privat"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 1dead61..93017bf 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Psaní poznámek"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Přidat"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Přidat widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Klepnutím změníte nastavení widgetu"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Změnit nastavení widgetu"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Hledat v aplikacích"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázáno administrátorem"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Povolit otáčení plochy"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Při otočení telefonu"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Puntíky s oznámením"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Zapnuto"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Vypnuto"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pozastavit pracovní aplikace"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Zrušit pozastavení"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtr"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Selhalo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Soukromý prostor"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 4851c52..c22d0dc 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Notetagning"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Tilføj"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Tilføj <xliff:g id="WIDGET_NAME">%1$s</xliff:g>-widget"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tryk for at ændre widgetindstillinger"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Skift widgetindstillinger"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Søg efter apps"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Deaktiveret af din administrator"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Tillad rotation af startskærmen"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Når telefonen roteres"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Notifikationsprikker"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Til"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Fra"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Sæt arbejdsapps på pause"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Genoptag"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mislykket: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privat område"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 38f943b..21ada28 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Notizen"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Hinzufügen"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Widget „<xliff:g id="WIDGET_NAME">%1$s</xliff:g>“ hinzufügen"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tippen, um die Widget-Einstellungen zu ändern"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Widget-Einstellungen ändern"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Apps finden"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Von deinem Administrator deaktiviert"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Drehen des Startbildschirms zulassen"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Beim Drehen des Smartphones"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"App-Benachrichtigungspunkte"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"An"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Aus"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Geschäftliche Apps pausieren"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Nicht mehr pausieren"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Fehler: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Vertrauliches Profil"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 9860f21..1e18977 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Δημιουργία σημειώσεων"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Προσθήκη"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Προσθήκη του γραφικού στοιχείου <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Πατήστε για αλλαγή των ρυθμίσεων του γραφικού στοιχείου"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Αλλαγή ρυθμίσεων γραφικού στοιχείου"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Αναζήτηση εφαρμογών"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Απενεργοποιήθηκε από τον διαχειριστή σας"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Να επιτρέπεται η περιστροφή της αρχικής οθόνης"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Όταν το τηλέφωνο περιστρέφεται"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Κουκκίδες ειδοποίησης"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Ενεργοποίηση"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Απενεργοποίηση"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Το κατάλαβα"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Παύση εφαρμογών εργασιών"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Αναίρεση παύσης"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Φίλτρο"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Αποτυχία: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Ιδιωτικός χώρος"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index f9711f0..a2a4145 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Note-taking"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Add"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Add <xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Change widget settings"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Allow home screen rotation"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Notification dots"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pause work apps"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Unpause"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Private space"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index f41ece6..c600680 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -69,6 +69,9 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Note-taking"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Add"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Add <xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
+ <string name="widgets_list_expand_button_label" msgid="7912016136574932622">"Show all"</string>
+ <string name="widgets_list_expand_button_content_description" msgid="4600513860973450888">"Show all widgets"</string>
+ <string name="widgets_list_expanded" msgid="7374857868788557730">"Showing all widgets"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Change widget settings"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
@@ -124,6 +127,8 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Allow home screen rotation"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
+ <string name="landscape_mode_title" msgid="5138814555934843926">"Landscape mode"</string>
+ <string name="landscape_mode_desc" msgid="7372569859592816793">"Set phone into landscape mode"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"Notification dots"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
@@ -188,6 +193,7 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Got it"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pause work apps"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Unpause"</string>
+ <string name="work_scheduler_button_content_description" msgid="917340740986764967">"Work apps schedule"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Private space"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index f9711f0..a2a4145 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Note-taking"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Add"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Add <xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Change widget settings"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Allow home screen rotation"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Notification dots"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pause work apps"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Unpause"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Private space"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index f9711f0..a2a4145 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Note-taking"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Add"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Add <xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Change widget settings"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Allow home screen rotation"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Notification dots"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pause work apps"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Unpause"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Private space"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index eae2009..44147c2 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Tomar notas"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Agregar"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Agregar widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Presiona para cambiar la configuración del widget"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Cambiar la configuración del widget"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Buscar apps"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"El administrador inhabilitó esta función"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Permitir la rotación de la pantalla principal"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Al girar el teléfono"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Puntos de notificación"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Activados"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Desactivados"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Entendido"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Detener apps de trabajo"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Reanudar"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtro"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Espacio privado"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 756912b..40e04d5 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Toma de notas"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Añadir"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Añadir widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toca para cambiar los ajustes del widget"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Cambiar ajustes del widget"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Buscar aplicaciones"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inhabilitado por el administrador"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Permitir rotación de la pantalla de inicio"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Al girar el teléfono"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Burbujas de notificación"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Activado"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Desactivadas"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Entendido"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pausar aplicaciones de trabajo"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Reanudar"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtro"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Se ha producido un error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Espacio privado"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index f44f45b..de62de1 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Märkmete tegemine"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Lisa"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Lisa vidin <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Puudutage vidina seadete muutmiseks"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Vidina seadete muutmine"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Otsige rakendusi"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Keelas administraator"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Luba avakuva pööramine"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Kui telefoni pööratakse"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Märguandetäpid"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Sees"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Väljas"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Selge"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Peata töörakendused"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Lõpeta peatamine"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nurjus: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privaatne ruum"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 5f5f76f..27510b3 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Oharrak idazteko"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Gehitu"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Gehitu <xliff:g id="WIDGET_NAME">%1$s</xliff:g> widgeta"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Sakatu hau widgeten ezarpenak aldatzeko"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Aldatu widgeten ezarpenak"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Bilatu aplikazioetan"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratzaileak desgaitu du"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Eman orri nagusia biratzeko baimena"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Telefonoa biratzean"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Jakinarazpen-biribiltxoak"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Aktibatuta"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Desaktibatuta"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Ados"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pausatu laneko aplikazioak"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Aktibatu berriro"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Iragazi"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Huts egin du: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Eremu pribatua"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index c5adfd6..8a21dc7 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"یادداشتبرداری"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"افزودن"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"افزودن ابزاره <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"برای تغییر تنظیمات ابزاره، تکضرب بزنید"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"تغییر تنظیمات ابزاره"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"جستجوی برنامهها"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"توسط سرپرست سیستم غیرفعال شده است"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"مجاز کردن چرخش صفحه اصلی"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"وقتی تلفن چرخانده میشود"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"نقطههای اعلان"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"روشن"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"خاموش"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"متوجهام"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"توقف موقت برنامههای کاری"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"ازسرگیری"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"فیلتر"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ناموفق بود: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"فضای خصوصی"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index ea5883b..c778a61 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Muistiinpanojen tekeminen"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Lisää"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Lisää widget: <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Napauta, niin voit muuttaa widgetin asetuksia"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Muuta widgetin asetuksia"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Hae sovelluksia"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Järjestelmänvalvoja on poistanut toiminnon käytöstä."</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Salli aloitusnäytön kiertäminen"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Kun puhelinta kierretään"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Pistemerkit"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Päällä"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Ei päällä"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Keskeytä työsovellusten käyttö"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Jatka"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Suodatin"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Epäonnistui: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Yksityinen tila"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index f58b644..1f5e264 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Prise de note"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Ajouter"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Ajoutez le widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Touchez pour modifier les paramètres du widget"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Modifier les paramètres du widget"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Rechercher dans les applis"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Cette fonction est désactivée par votre administrateur"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Autoriser la rotation de l\'écran d\'accueil"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Lorsque vous faites pivoter le téléphone"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Pastilles de notification"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Activé"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Désactivé"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Mettre en pause les applis professionnelles"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Réactiver"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrer"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Échec : <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Espace privé"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index feac922..a5b9a32 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Prise de notes"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Ajouter"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Ajoutez un widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Appuyez pour modifier les paramètres du widget"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Modifier les paramètres du widget"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Rechercher dans les applications"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Désactivé par votre administrateur"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Autoriser la rotation de l\'écran d\'accueil"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Lorsque vous faites pivoter le téléphone"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Pastilles de notification"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Activées"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Désactivées"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Mettre en pause les applis professionnelles"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Réactiver"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtre"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Échec : <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Espace privé"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index c36bf18..ca2861a 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Toma de notas"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Engadir"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Engadir o widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toca para cambiar a configuración do widget"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Cambiar configuración do widget"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Buscar aplicacións"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Función desactivada polo administrador"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Permitir xirar a pantalla de inicio"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Ao xirar o teléfono"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Puntos de notificacións"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Opción activada"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Desactivados"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Entendido"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pór en pausa aplicacións do traballo"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Volver activar"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtra"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Erro: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Espazo privado"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index ee0dec7..59e3134 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"નોંધ લેવી"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"ઉમેરો"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> વિજેટ ઉમેરો"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"વિજેટના સેટિંગ બદલવા માટે ટૅપ કરો"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"વિજેટના સેટિંગ બદલો"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ઍપ શોધો"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"તમારા વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"હોમ સ્ક્રીનને ફેરવવાની મંજૂરી આપો"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"જ્યારે ફોન ફેરવવામાં આવે ત્યારે"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"નોટિફિકેશન માટેના ચિહ્નો"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"ચાલુ છે"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"બંધ છે"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"સમજાઈ ગયું"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ઑફિસની ઍપ થોભાવો"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"ફરી ચાલુ કરો"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ફિલ્ટર કરો"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"નિષ્ફળ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"ખાનગી સ્પેસ"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index c57bad3..c26cafb 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"नोट बनाने से जुड़े विजेट"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"जोड़ें"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट जोड़ें"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"विजेट की सेटिंग में बदलाव करने के लिए टैप करें"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"विजेट की सेटिंग में बदलाव करें"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ऐप्लिकेशन खोजें"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपके एडमिन ने बंद किया हुआ है"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"होम स्क्रीन घुमाने की अनुमति दें"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"फ़ोन घुुमाए जाने पर"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"सूचनाएं बताने वाला डॉट"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"चालू है"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"चालू"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ठीक है"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"वर्क ऐप्लिकेशन रोकें"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"चालू करें"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"फ़िल्टर"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"पूरा नहीं हुआ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"प्राइवेट स्पेस"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 26c9155..4a29979 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Pisanje bilježaka"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Dodaj"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Dodaj widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Dodirnite da biste promijenili postavke widgeta"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Promijenite postavke widgeta"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pretraži aplikacije"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio administrator"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Dopusti zakretanje početnog zaslona"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon zakrene"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Točke obavijesti"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Uključeno"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Isključeno"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Shvaćam"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pauziraj poslovne aplikacije"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Ponovno pokreni"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrirajte"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nije uspjelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privatni prostor"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index dd50451..aa1167d 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Jegyzetelés"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Hozzáadás"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> modul hozzáadása"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Ide koppintva módosíthatja a modulbeállításokat"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"A modulbeállítások módosítása"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Alkalmazások keresése"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"A rendszergazda letiltotta"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"A kezdőképernyő elforgatásának engedélyezése"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"A telefon elforgatásakor"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Értesítési pöttyök"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Be"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Ki"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Értem"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Munkahelyi alkalmazások szüneteltetése"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Folytatás"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Szűrő"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Sikertelen: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privát terület"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 74ea382..692984e 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Նշումների ստեղծում"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Ավելացնել"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Ավելացնել <xliff:g id="WIDGET_NAME">%1$s</xliff:g> վիջեթը"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Հպեք՝ վիջեթի կարգավորումները փոփոխելու համար"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Փոխել վիջեթի կարգավորումները"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Որոնել հավելվածներ"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Անջատվել է ձեր ադմինիստրատորի կողմից"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Թույլ տալ հիմնական էկրանի պտտումը"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Հեռախոսը պտտելու դեպքում"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Ծանուցումների կետիկներ"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Միացված է"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Անջատված է"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Եղավ"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Դադարեցնել աշխատանքային հավելվածները"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Վերսկսել"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Զտեք"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Չհաջողվեց կատարել գործողությունը (<xliff:g id="WHAT">%1$s</xliff:g>)"</string>
<string name="private_space_label" msgid="2359721649407947001">"Անձնական տարածք"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 296b12e..1870055 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Pembuatan catatan"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Tambahkan"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Tambahkan widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Ketuk untuk mengubah setelan widget"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Ubah setelan widget"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Telusuri aplikasi"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dinonaktifkan oleh admin"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Izinkan layar utama diputar"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Saat ponsel diputar"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Titik notifikasi"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Aktif"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Nonaktif"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Oke"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Jeda aplikasi kerja"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Aktifkan lagi"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Gagal: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Ruang privasi"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index ad1742d..393589a 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Glósugerð"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Bæta við"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Bæta græjunni <xliff:g id="WIDGET_NAME">%1$s</xliff:g> við"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Ýttu til að breyta græjustillingum"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Breyta græjustillingum"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Leita í forritum"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gert óvirkt af kerfisstjóra"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Leyfa snúning á heimaskjá"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Þegar símanum er snúið"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Tilkynningapunktar"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Kveikt"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Slökkt"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Ég skil"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Setja vinnuforrit í bið"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Ljúka hléi"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Sía"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mistókst: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Leynirými"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 3177dba..2a14445 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Aggiunta di note"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Aggiungi"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Aggiungi widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tocca per modificare le impostazioni del widget"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Modifica le impostazioni del widget"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Cerca nelle app"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disattivata dall\'amministratore"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Consenti rotazione della schermata Home"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Con il telefono ruotato"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Indicatori di notifica"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Metti in pausa le app di lavoro"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Riattiva"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtra"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Operazione non riuscita: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Spazio privato"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 823e915..724742a 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"כתיבת הערות"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"הוספה"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"הוספת הווידג\'ט <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"אפשר לשנות את הגדרות הווידג\'ט בהקשה"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"שינוי הגדרות הווידג\'ט"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"חיפוש אפליקציות"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"הושבת על ידי מנהל המערכת שלך"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"סיבוב מסך הבית"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"כאשר מסובבים את הטלפון"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"סימני ההתראות"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"מופעל"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"כבוי"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"הבנתי"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"השהיית האפליקציות לעבודה"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"ביטול ההשהיה"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"סינון"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"הפעולה נכשלה: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"מרחב פרטי"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 0ffd066..29dbf26 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"メモ"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"追加"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>ウィジェットを追加"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"タップしてウィジェットの設定を変更する"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ウィジェットの設定を変更します"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"アプリを検索"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"管理者により無効にされています"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"ホーム画面の回転を許可"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"スマートフォンの向きに合わせます"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"通知ドット"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"ON"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"OFF"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"仕事用アプリを一時停止"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"停止解除"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"フィルタ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"失敗: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"プライベート スペース"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index a2b83df..876ab11 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"ჩანიშვნა"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"დამატება"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ვიჯეტის დამატება"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"შეეხეთ ვიჯეტის პარამეტრების შესაცვლელად"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ვიჯეტის პარამეტრების შეცვლა"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"აპების ძიება"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"გათიშულია თქვენი ადმინისტრატორის მიერ"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"მთავარი ეკრანის შეტრიალების დაშვება"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"ტელეფონის შეტრიალებისას"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"შეტყობინების ნიშნულები"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"ჩართულია"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"გამორთულია"</string>
@@ -188,11 +198,13 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"გასაგებია"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"სამსახურის აპების დაპაუზება"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"პაუზის გაუქმება"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ფილტრი"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ვერ მოხერხდა: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"პირადი სივრცე"</string>
<string name="private_space_secondary_label" msgid="9203933341714508907">"დასაყენებლად ან გასახსნელად შეეხეთ"</string>
- <string name="ps_container_title" msgid="4391796149519594205">"პირადი"</string>
+ <string name="ps_container_title" msgid="4391796149519594205">"კერძო"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"პირადი სივრცის პარამეტრები"</string>
<string name="ps_container_unlock_button_content_description" msgid="9181551784092204234">"პირადი (განბლოკილი)."</string>
<string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"პირადი (ჩაკეტილი)."</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 0e5472c..bc1c380 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Ескертпе жазу"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Қосу"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Виджет (<xliff:g id="WIDGET_NAME">%1$s</xliff:g>) қосу"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Виджет параметрлерін өзгерту үшін түртіңіз."</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Виджет параметрлерін өзгерту"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Қолданбаларды іздеу"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Әкімші өшірді"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Негізгі экранды бұруға рұқсат ету"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Телефон бұрылғанда"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Хабарландыру белгілері"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Қосулы"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Өшірулі"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Түсінікті"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Жұмыс қолданбаларын кідірту"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Қайта қосу"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Сүзгі"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Қате шықты: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Құпия кеңістік"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 1f06e35..813b5a2 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"ការកត់ត្រា"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"បញ្ចូល"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"បញ្ចូលធាតុក្រាហ្វិក <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ចុចដើម្បីប្ដូរការកំណត់ធាតុក្រាហ្វិក"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ប្ដូរការកំណត់ធាតុក្រាហ្វិក"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ស្វែងរកកម្មវិធី"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"បានបិទដំណើរការដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"អនុញ្ញាតការបងិ្វលអេក្រង់ដើម"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"នៅពេលដែលបង្វិលទូរសព្ទ"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"ស្លាកជូនដំណឹង"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"បើក"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"បិទ"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"យល់ហើយ"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ផ្អាកកម្មវិធីការងារ"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"ឈប់ផ្អាក"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"តម្រង"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"បានបរាជ័យ៖ <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"បន្ទប់ឯកជន"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 08b4e47..b4bc3a1 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"ಟಿಪ್ಪಣಿ ತೆಗೆದುಕೊಳ್ಳುವುದು"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"ಸೇರಿಸಿ"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ವಿಜೆಟ್ ಸೇರಿಸಿ"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ವಿಜೆಟ್ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ವಿಜೆಟ್ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಬದಲಾಯಿಸಿ"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ಆ್ಯಪ್ಗಳನ್ನು ಹುಡುಕಿ"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸಿ"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"ಫೋನ್ ತಿರುಗಿಸಿದಾಗ"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"ನೋಟಿಫಿಕೇಶನ್ ಡಾಟ್ಗಳು"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"ಆನ್ ಆಗಿದೆ"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"ಆಫ್ ಆಗಿದೆ"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ಅರ್ಥವಾಯಿತು"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್ಗಳನ್ನು ವಿರಾಮಗೊಳಿಸಿ"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"ವಿರಾಮವನ್ನು ರದ್ದುಗೊಳಿಸಿ"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ಫಿಲ್ಟರ್"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ವಿಫಲವಾಗಿದೆ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"ಖಾಸಗಿ ಸ್ಪೇಸ್"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 2130382..4baba23 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"메모"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"추가"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> 위젯 추가"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"탭하여 위젯 설정 변경"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"위젯 설정 변경"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"앱 검색"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"관리자가 사용 중지함"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"홈 화면 회전 허용"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"휴대전화 회전 시"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"알림 표시 점"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"사용"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"사용 안함"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"확인"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"직장 앱 일시중지"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"일시중지 해제"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"필터"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"실패: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"비공개 스페이스"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index b803fa5..83b0fa8 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Эскертме жазуу"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Кошуу"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджетин кошуу"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Виджеттин параметрлерин өзгөртүү үчүн таптап коюңуз"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Виджеттин параметрлерин өзгөртүү"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Колдонмолорду издөө"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администраторуңуз өчүрүп койгон"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Башкы экранды бурууга уруксат берүү"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Телефон бурулганда"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Билдирмелер белгилери"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Күйүк"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Өчүк"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Түшүндүм"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Жумуш колдонмолорун тындыруу"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Улантуу"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Чыпкалоо"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Аткарылган жок: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Жеке мейкиндик"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index d90f563..767c574 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"ການຈົດບັນທຶກ"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"ເພີ່ມ"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"ເພີ່ມວິດເຈັດ <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ແຕະເພື່ອປ່ຽນການຕັ້ງຄ່າວິດເຈັດ"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ປ່ຽນການຕັ້ງຄ່າວິດເຈັດ"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ຊອກຫາແອັບ"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ຖືກປິດການນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"ອະນຸຍາດໃຫ້ໝຸນໜ້າຈໍຢູ່ໂຮມສະກຣີນໄດ້"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"ເມື່ອໝຸນໂທລະສັບ"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"ຈຸດການແຈ້ງເຕືອນ"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"ເປີດ"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"ປິດ"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ເຂົ້າໃຈແລ້ວ"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ຢຸດແອັບບ່ອນເຮັດວຽກຊົ່ວຄາວ"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"ຍົກເລີກການຢຸດຊົ່ວຄາວ"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ກັ່ນຕອງ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ບໍ່ສຳເລັດ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"ພື້ນທີ່ສ່ວນຕົວ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index b9495db..08b603d 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Užrašų kūrimas"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Pridėti"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Pridėti valdiklį: <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Palieskite, kad pakeistumėte valdiklio nustatymus"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Pakeisti valdiklio nustatymus"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Paieškos programos"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Išjungė administratorius"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Leisti pasukti pagrindinį ekraną"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Kai telefonas pasukamas"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Pranešimų taškai"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Įjungta"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Išjungta"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Supratau"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pristabdyti darbo programas"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Atšaukti pristabdymą"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtruoti"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nepavyko: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privati erdvė"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 1c3faf4..e02abe0 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Piezīmju pierakstīšana"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Pievienot"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Pievienot logrīku <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Pieskarieties, lai mainītu logrīka iestatījumus."</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Mainīt logrīka iestatījumus"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Meklēt lietotnes"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Atspējojis administrators"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Atļaut sākuma ekrāna pagriešanu"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Pagriežot tālruni"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Paziņojumu punkti"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Ieslēgti"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Izslēgts"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Labi"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pārtraukt darba lietotņu darbību"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Atsākt"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrs"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Neizdevās: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privātā telpa"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index cf3363e..e524d82 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Фаќање белешки"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Додај"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Додај го виџетот <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Допрете за да ги промените поставките за виџетот"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Промени ги поставките за виџетот"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Пребарувајте апликации"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Оневозможено од администраторот"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Дозволи ротирање на почетниот екран"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Кога телефонот се ротира"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Точки за известување"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Вклучено"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Исклучено"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Сфатив"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Паузирај ги работните апликации"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Прекини ја паузата"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтер"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Не успеа: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Приватен простор"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index dd6aa71..e273e14 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"കുറിപ്പ് രേഖപ്പെടുത്തൽ"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"ചേർക്കുക"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> വിജറ്റ് ചേർക്കുക"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"വിജറ്റ് ക്രമീകരണം മാറ്റാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"വിജറ്റ് ക്രമീകരണം മാറ്റുക"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ആപ്പുകൾ തിരയുക"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"അഡ്മിൻ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"ഹോം സ്ക്രീൻ റൊട്ടേഷൻ അനുവദിക്കുക"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"ഫോൺ തിരിച്ച നിലയിലായിരിക്കുമ്പോൾ"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"അറിയിപ്പ് ഡോട്ടുകൾ"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"ഓണാണ്"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"ഓഫാണ്"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"മനസ്സിലായി"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ഔദ്യോഗിക ആപ്പുകൾ താൽക്കാലികമായി നിർത്തുക"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"താൽക്കാലികമായി നിർത്തിയത് മാറ്റുക"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ഫിൽട്ടർ ചെയ്യുക"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"പരാജയപ്പെട്ടു: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"സ്വകാര്യ സ്പേസ്"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 659f82c..7e5ed73 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Тэмдэглэл хөтлөх"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Нэмэх"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджетийг нэмэх"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Жижиг хэрэгслийн тохиргоог өөрчлөхийн тулд товшино уу"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Жижиг хэрэгслийн тохиргоог өөрчлөх"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Апп хайх"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Таны админ идэвхгүй болгосон"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Үндсэн нүүрийг эргүүлэхийг зөвшөөрөх"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Утсыг эргүүлсэн үед"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Мэдэгдлийн цэг"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Асаалттай"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Унтраалттай"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Ойлголоо"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Ажлын аппуудыг түр зогсоох"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Түр зогсоохоо болих"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Шүүлтүүр"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Амжилтгүй болсон: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Хувийн орон зай"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index aa4b433..b5d4f71 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"टिपा घेणे"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"जोडा"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट जोडा"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"विजेट सेटिंग्ज बदलण्यासाठी टॅप करा"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"विजेट सेटिंग्ज बदला"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"अॅप्स शोधा"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपल्या प्रशासकाने अक्षम केले"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"होम स्क्रीन फिरवण्याची अनुमती द्या"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"फोन फिरवला जातो तेव्हा"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"नोटिफिकेशन डॉट"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"सुरू"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"बंद"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"समजले"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"कार्य ॲप्स थांबवा"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"अनपॉझ करा"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"फिल्टर"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"हे करता आले नाही: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"खाजगी स्पेस"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 89345ce..a48d518 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Pengambilan nota"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Tambah"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Tambahkan widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Ketik untuk menukar tetapan widget"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Tukar tetapan widget"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Cari apl"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dilumpuhkan oleh pentadbir anda"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Benarkan putaran skrin utama"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Apabila telefon diputar"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Titik pemberitahuan"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Hidup"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Mati"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Jeda apl kerja"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Nyahjeda"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Tapis"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Gagal: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Ruang persendirian"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 7d5d275..f892a1b 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"မှတ်စုလိုက်ခြင်း"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"ထည့်ရန်"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ဝိဂျက်ထည့်ရန်"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ဝိဂျက် ဆက်တင်များကို ပြောင်းရန် တို့ပါ"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ဝိဂျက် ဆက်တင်များကို ပြောင်းပါ"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ရှာဖွေမှု အက်ပ်များ"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"သင့်စီမံခန့်ခွဲသူက ပိတ်လိုက်ပါသည်"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"ပင်မစာမျက်နှာလှည့်ခြင်းကို ခွင့်ပြုခြင်း"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"ဖုန်းကိုလှည့်ထားစဉ်"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"သတိပေးချက် အစက်များ"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"ဖွင့်"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"ပိတ်"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"နားလည်ပြီ"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"အလုပ်သုံးအက်ပ်များကို ခဏရပ်ရန်"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"ပြန်ဖွင့်ရန်"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"စစ်ထုတ်ရန်"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"မအောင်မြင်ပါ− <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"သီးသန့်ချတ်ခန်း"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index bb92d90..b9b69a0 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Notatskriving"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Legg til"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Legg til <xliff:g id="WIDGET_NAME">%1$s</xliff:g>-modulen"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Trykk for å endre modulinnstillinger"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Endre modulinnstillinger"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Søk etter apper"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratoren har slått av funksjonen"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Tillat at startskjermen roterer"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Når telefonen roteres"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Varselsprikker"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"På"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Av"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Greit"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Sett jobbapper på pause"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Gjenoppta"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mislyktes: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privat område"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 49efec9..6f93761 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"नोट लेख्ने कार्य"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"हाल्नुहोस्"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट हाल्नुहोस्"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"विजेटका सेटिङ बदल्न ट्याप गर्नुहोस्"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"विजेटका सेटिङ बदल्नुहोस्"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"एपहरू खोज्नुहोस्"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"तपाईँको प्रशासकद्वारा असक्षम गरिएको"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"होम स्क्रिन रोटेट हुन दिइयोस्"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"फोन घुमाउँदा"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"नोटिफिकेसन डट"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"सक्रिय"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"निष्क्रिय"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"बुझेँ"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"कामसम्बन्धी एपहरू पज गर्नुहोस्"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"सुचारु गर्नुहोस्"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"फिल्टर"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"कार्य पूरा गर्न सकिएन: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"निजी स्पेस"</string>
diff --git a/res/values-night/styles.xml b/res/values-night/styles.xml
index 06f0eee..a891e39 100644
--- a/res/values-night/styles.xml
+++ b/res/values-night/styles.xml
@@ -38,16 +38,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorInverseOnSurface">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorInversePrimary">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorInverseSurface">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index d898270..a38971f 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Aantekeningen maken"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Toevoegen"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> toevoegen"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tik om de widgetinstellingen te wijzigen"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Widgetinstellingen wijzigen"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Apps zoeken"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Uitgezet door je beheerder"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Draaien van startscherm toestaan"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Als de telefoon gedraaid is"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Meldingsstipjes"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Aan"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Uit"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Werk-apps pauzeren"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Hervatten"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filteren"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mislukt: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privéruimte"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 788b168..a20e0fc 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"ନୋଟ-ଟେକିଂ"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"ଯୋଗ କରନ୍ତୁ"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ୱିଜେଟ ଯୋଗ କରନ୍ତୁ"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ୱିଜେଟ ସେଟିଂସ ପରିବର୍ତ୍ତନ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ୱିଜେଟ ସେଟିଂସ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ଆପ ସର୍ଚ୍ଚ କରନ୍ତୁ"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ଆପଣଙ୍କ ଆଡମିନଙ୍କ ଦ୍ୱାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"ହୋମ ସ୍କ୍ରିନ ରୋଟେସନକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"ଯେତେବେଳେ ଫୋନକୁ ରୋଟେଟ କରାଯାଇଥାଏ"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"ବିଜ୍ଞପ୍ତି ଡଟ୍ସ"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"ଚାଲୁ"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"ବନ୍ଦ କରନ୍ତୁ"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ବୁଝିଗଲି"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ୱାର୍କ ଆପ୍ସ ବିରତ କରନ୍ତୁ"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ଫିଲ୍ଟର୍"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ବିଫଳ ହୋଇଛି: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 4dc26aa..65187bf 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"ਨੋਟ ਬਣਾਉਣਾ"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ਵਿਜੇਟ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ਵਿਜੇਟ ਸੈਟਿੰਗਾਂ ਨੂੰ ਬਦਲਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ਵਿਜੇਟ ਸੈਟਿੰਗਾਂ ਬਦਲੋ"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ਐਪਾਂ ਖੋਜੋ"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"ਹੋਮ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"ਜਦੋਂ ਫ਼ੋਨ ਘੁਮਾਇਆ ਜਾਂਦਾ ਹੈ"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"ਸੂਚਨਾ ਬਿੰਦੂ"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"ਚਾਲੂ"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"ਬੰਦ"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ਸਮਝ ਲਿਆ"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਰੋਕੋ"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"ਰੋਕ ਹਟਾਓ"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ਫਿਲਟਰ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ਇਹ ਕਾਰਵਾਈ ਅਸਫਲ ਹੋਈ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 1b0f841..861be1e 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Notatki"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Dodaj"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Dodaj widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Kliknij, aby zmienić ustawienia widżetu"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Zmień ustawienia widżetu"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Wyszukaj aplikacje"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Funkcja wyłączona przez administratora"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Zezwalaj na obrót ekranu głównego"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Po obróceniu telefonu"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Kropki powiadomień"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Włączone"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Wyłączone"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Wstrzymaj aplikacje służbowe"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Cofnij wstrzymywanie"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtruj"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Niepowodzenie: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Przestrzeń prywatna"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 2cb3c8f..04bff1f 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Tomar notas"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Adicionar"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Adicione o widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toque para alterar as definições do widget"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Alterar definições do widget"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pesquisar apps"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativada pelo gestor"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Permitir rotação do ecrã principal"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Quando o telemóvel é rodado"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Pontos de notificação"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Ativados"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Desativados"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pausar apps de trabalho"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Retomar"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrar"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Falhou: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Espaço privado"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 414f55d..20cc9fb 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Anotações"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Adicionar"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Adicionar o widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toque para mudar as configurações do widget"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Mudar as configurações do widget"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pesquisar apps"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativado pelo administrador"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Permitir a rotação da tela inicial"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Quando o smartphone for girado"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Pontos de notificação"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Ativados"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Desativado"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Ok"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pausar apps de trabalho"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Ativar"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrar"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Falha: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Espaço privado"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 24e6cde..4226920 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Luare de notițe"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Adaugă"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Adaugă widgetul <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Atinge ca să schimbi setările pentru widgeturi"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Modifică setările pentru widgeturi"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Caută aplicații"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dezactivată de administrator"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Permite rotirea ecranului de pornire"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Când telefonul este rotit"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Puncte de notificare"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Activate"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Dezactivate"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Întrerupe aplicațiile pentru lucru"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Anulează întreruperea"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtru"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Eșuare: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Spațiu privat"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index f94fd59..2b827ef 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Создание заметок"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Добавить"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Добавить виджет \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\""</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Нажмите, чтобы изменить настройки виджета"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Изменить настройки виджета"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Поиск приложений"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Функция отключена администратором"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Разрешить поворачивать главный экран"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"При повороте телефона"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Значки уведомлений"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Включены"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Отключены"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ОК"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Приостановить рабочие приложения"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Возобновить"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Фильтр"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Не удалось выполнить действие (<xliff:g id="WHAT">%1$s</xliff:g>)."</string>
<string name="private_space_label" msgid="2359721649407947001">"Частное пространство"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 1938c54..646443e 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"සටහන් කර ගැනීම"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"එක් කරන්න"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> විජට්ටුව එක් කරන්න"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"විජට් සැකසීම් වෙනස් කිරීමට තට්ටු කරන්න"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"විජට් සැකසීම් වෙනස් කරන්න"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"යෙදුම් සොයන්න"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ඔබගේ පරිපාලක විසින් අබල කරන ලදී"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"මුල් තිරය කරකැවීමට ඉඩ දෙන්න"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"දුරකථනය කරකවන විට"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"දැනුම්දීම් තිත්"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"ක්රියාත්මකයි"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"ක්රියාවිරහිතයි"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"තේරුණා"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"කාර්යාල යෙදුම් විරාම කරන්න"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"විරාම නොකරන්න"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"පෙරහන"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"අසාර්ථකයි: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"පෞද්ගලික ඉඩ"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 528ba28..f9245cd 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Zapisovanie poznámok"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Pridať"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Pridať miniaplikáciu <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Klepnutím zmeňte nastavenia miniaplikácie"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Zmena nastavení miniaplikácie"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Hľadať aplikácie"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázané vaším správcom"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Povoliť otáčanie plochy"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Pri otočení telefónu"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Bodky upozornení"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Zapnuté"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Vypnuté"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Dobre"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pozastaviť pracovné aplikácie"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Zrušiť pozastavenie"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrujte"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Zlyhalo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Súkromný priestor"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index e0288c4..e8d8702 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Ustvarjanje zapiskov"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Dodaj"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Dodajanje pripomočka »<xliff:g id="WIDGET_NAME">%1$s</xliff:g>«"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Dotaknite se, če želite spremeniti nastavitve pripomočka."</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Spreminjanje nastavitev pripomočka"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Iskanje programov"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogočil skrbnik."</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Dovoli sukanje začetnega zaslona"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Ko se telefon zasuka"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Obvestilne pike"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Vklopljeno"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Izklopljeno"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"V redu"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Začasno zaustavi delovne aplikacije"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Znova aktiviraj"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtriranje"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Ni uspelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Zasebni prostor"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 96b1bff..1e5420a 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Mbajtja e shënimeve"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Shto"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Shto miniaplikacionin <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Trokit për të ndryshuar cilësimet e miniaplikacionit"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Ndrysho cilësimet e miniaplikacionit"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Kërko për aplikacione"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Çaktivizuar nga administratori"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Lejo rrotullimin e ekranit bazë"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Kur telefoni rrotullohet"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Pikat e njoftimeve"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Aktiv"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Joaktiv"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"E kuptova"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Vendos në pauzë aplikacionet e punës"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Hiq nga pauza"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtro"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Dështoi: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Hapësira private"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 054ddff..c5b7bd0 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Прављење бележака"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Додај"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Додајте виџет <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Додирните да бисте променили подешавања виџета"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Промените подешавања виџета"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Претражите апликације"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администратор је онемогућио"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Дозволи ротацију почетног екрана"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Када се телефон ротира"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Тачке за обавештења"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Укључено"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Искључено"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Важи"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Паузирај пословне апликације"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Поново активирај"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтер"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Није успело: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Приватни простор"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 482cd88..9ebf366 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Anteckna"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Lägg till"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Lägg till widgeten <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tryck för att ändra inställningarna för widgeten"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Ändra inställningarna för widgeten"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Sök efter appar"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inaktiverat av administratören"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Tillåt rotering av startskärmen"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"När telefonen vrids"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Aviseringsprickar"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"På"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Av"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pausa jobbappar"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Återuppta"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Misslyckades: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privat rum"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 015ff8d..a7725f3 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Kuandika madokezo"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Weka"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Weka wijeti ya <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Gusa ili ubadilishe mipangilio ya wijeti"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Badilisha mipangilio ya wijeti"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Tafuta programu"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Imezimwa na msimamizi wako"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Ruhusu kipengele cha kuzungusha skrini ya kwanza"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Simu inapozungushwa"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Vitone vya arifa"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Imewashwa"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Imezimwa"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Nimeelewa"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Simamisha programu za kazini"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Acha kusimamisha"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Kichujio"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Hitilafu: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Nafasi ya faragha"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 704361a..295367d 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"குறிப்பெடுத்தல்"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"சேர்"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> விட்ஜெட்டைச் சேர்க்கும்"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"விட்ஜெட் அமைப்புகளை மாற்றத் தட்டவும்"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"விட்ஜெட் அமைப்புகளை மாற்றும்"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ஆப்ஸில் தேடுக"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"உங்கள் நிர்வாகி முடக்கியுள்ளார்"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"முகப்புத் திரை சுழற்சியை அனுமதித்தல்"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"மொபைலைச் சுழற்றும் போது"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"அறிவிப்புப் புள்ளிகள்"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"ஆன்"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"ஆஃப்"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"சரி"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"பணி ஆப்ஸை இடைநிறுத்து"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"மீண்டும் இயக்கு"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"வடிப்பான்"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"தோல்வி: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"தனிப்பட்ட சேமிப்பிடம்"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 4550961..5859665 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"నోట్-టేకింగ్"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"జోడించండి"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> విడ్జెట్ను జోడించండి"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"విడ్జెట్ సెట్టింగ్లను మార్చడానికి ట్యాప్ చేయండి"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"విడ్జెట్ సెట్టింగ్లను మార్చండి"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"యాప్ల కోసం సెర్చ్ చేయండి"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"మీ నిర్వాహకులు నిలిపివేసారు"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"మొదటి స్క్రీన్ రొటేషన్ను అనుమతించండి"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"ఫోన్ను తిప్పినప్పుడు"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"నోటిఫికేషన్ డాట్లు"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"ఆన్"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"ఆఫ్"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"అర్థమైంది"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"వర్క్ యాప్లను పాజ్ చేయండి"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"పాజ్ నుండి తీసివేయండి"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ఫిల్టర్ చేయి"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"విఫలమైంది: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"ప్రైవేట్ స్పేస్"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 27cba7f..fd2ba21 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"การจดบันทึก"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"เพิ่ม"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"เพิ่มวิดเจ็ต <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"แตะเพื่อเปลี่ยนการตั้งค่าวิดเจ็ต"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"เปลี่ยนการตั้งค่าวิดเจ็ต"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ค้นหาแอป"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ปิดใช้โดยผู้ดูแลระบบ"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"อนุญาตให้หมุนหน้าจอหลัก"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"เมื่อหมุนโทรศัพท์"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"เครื่องหมายจุดแสดงการแจ้งเตือน"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"เปิด"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"ปิด"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"รับทราบ"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"หยุดแอปงานชั่วคราว"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"ยกเลิกการหยุดชั่วคราว"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ตัวกรอง"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ไม่สำเร็จ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"พื้นที่ส่วนตัว"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 5dea339..69f88e1 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Pagtatala"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Idagdag"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Idagdag ang widget na <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"I-tap para baguhin ang mga setting ng widget"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Baguhin ang mga setting ng widget"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Maghanap ng mga app"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Na-disable ng iyong admin"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Payagan ang pag-rotate ng home screen"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Kailan maro-rotate ang telepono"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Mga notification dot"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Naka-on"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Naka-off"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"I-pause ang mga app para sa trabaho"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"I-unpause"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Hindi nagawa: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Pribadong space"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 6b2cb60..e61b670 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Not alma"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Ekle"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget\'ı ekle"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Widget ayarlarını değiştirmek için dokunun"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Widget ayarlarını değiştir"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Uygulamalarda ara"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Yöneticiniz tarafından devre dışı bırakıldı"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Ana ekranı döndürmeye izin ver"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon döndürüldüğünde"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Bildirim noktaları"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Açık"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Kapalı"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Anladım"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"İş uygulamalarını duraklat"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Devam ettir"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtre"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Başarısız: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Gizli alan"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 3b35bc8..fd293e3 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Створення нотаток"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Додати"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Додати віджет \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\""</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Натисніть, щоб змінити налаштування віджета"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Змінити налаштування віджета"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Пошук додатків"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Вимкнув адміністратор"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Дозволити обертання головного екрана"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Коли телефон обертається"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Значки сповіщень"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Увімкнено"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Вимкнено"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Зрозуміло"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Призупинити робочі додатки"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Відновити"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Фільтр"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Не вдалося <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Приватний простір"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 8d158ce..911a2d5 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"نوٹ لکھنا"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"شامل کریں"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ویجیٹ شامل کریں"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ویجیٹ ترتیبات تبدیل کرنے کے لیے تھپتھپائیں"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ویجیٹ ترتیبات تبدیل کریں"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ایپس تلاش کریں"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"آپ کے منتظم کی طرف سے غیر فعال کر دیا گیا"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"ہوم اسکرین گھمانے کی اجازت دیں"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"جب فون گھمایا جاتا ہے"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"اطلاعاتی ڈاٹس"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"آن ہے"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"آف ہے"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"سمجھ آ گئی"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ورک ایپس موقوف کریں"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"چلائیں"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"فلٹر"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ناکام ہو گيا: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"نجی اسپیس"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 511d44b..aacb35f 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Qayd olish"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Chiqarish"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidjetini chiqarish"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Vidjet sozlamalarini oʻzgartirish uchun bosing"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Vidjet sozlamalarini oʻzgartirish"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Ilovalarni qidirish"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator tomonidan o‘chirilgan"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Bosh ekranni burishga ruxsat"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon burilganda"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Bildirishnoma belgilari"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Yoniq"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Oʻchiq"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Ishga oid ilovalarni pauza qilish"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Pauzadan chiqarish"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Saralash"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Xato: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Shaxsiy xona"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index d1f2adc..cea93da 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Ghi chú"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Thêm"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Thêm tiện ích <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Nhấn để thay đổi chế độ cài đặt tiện ích"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Thay đổi chế độ cài đặt tiện ích"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Tìm kiếm ứng dụng"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Bị tắt bởi quản trị viên của bạn"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Cho phép xoay màn hình chính"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Khi xoay điện thoại"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Dấu chấm thông báo"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Đang bật"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Tắt"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Tôi hiểu"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Tạm dừng các ứng dụng công việc"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Bỏ tạm dừng"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Bộ lọc"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Không thực hiện được thao tác: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Không gian riêng tư"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index ed30c33..9efd6c3 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"记事"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"添加"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"添加“<xliff:g id="WIDGET_NAME">%1$s</xliff:g>”微件"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"点按即可更改微件设置"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"更改微件设置"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"搜索应用"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"已被您的管理员停用"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"允许旋转主屏幕"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"手机旋转时"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"通知圆点"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"已开启"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"已关闭"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"知道了"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"暂停工作应用"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"取消暂停"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"过滤器"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"失败:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"私密空间"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 1ef3070..c944240 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"做筆記"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"新增"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"加<xliff:g id="WIDGET_NAME">%1$s</xliff:g>小工具"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"輕按即可變更小工具設定"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"變更小工具設定"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"搜尋應用程式"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由你的管理員停用"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"允許旋轉主畫面"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"隨手機旋轉"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"通知圓點"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"開啟"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"關閉"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"知道了"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"暫停工作應用程式"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"取消暫停"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"篩選器"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"操作失敗:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"私人空間"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index fa1b347..476c0e5 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"做筆記"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"新增"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"新增「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」小工具"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"輕觸即可變更小工具設定"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"變更小工具設定"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"搜尋應用程式"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由你的管理員停用"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"允許旋轉主畫面"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"當手機旋轉時"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"通知圓點"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"開啟"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"關閉"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"我知道了"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"暫停工作應用程式"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"取消暫停"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"篩選器"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"失敗:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"私人空間"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index c6753fc..623454b 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -69,6 +69,12 @@
<string name="widget_category_note_taking" msgid="3469689394504266039">"Ukuthatha amanothi"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Engeza"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"Engeza iwijethi ye-<xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for widgets_list_expand_button_label (7912016136574932622) -->
+ <skip />
+ <!-- no translation found for widgets_list_expand_button_content_description (4600513860973450888) -->
+ <skip />
+ <!-- no translation found for widgets_list_expanded (7374857868788557730) -->
+ <skip />
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Thepha ukuze ushintshe amasethingi ewijethi"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Shintsha amasethingi ewijethi"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Sesha izinhlelo zokusebenza"</string>
@@ -124,6 +130,10 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Kukhutshazwe umlawuli wakho"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Vumela ukuzungezisa kwesikrini sasekhaya"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Uma ifoni iphendukiswa"</string>
+ <!-- no translation found for landscape_mode_title (5138814555934843926) -->
+ <skip />
+ <!-- no translation found for landscape_mode_desc (7372569859592816793) -->
+ <skip />
<string name="notification_dots_title" msgid="9062440428204120317">"Amacashazi esaziso"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Vuliwe"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Valiwe"</string>
@@ -188,6 +198,8 @@
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Ngiyezwa"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Misa ama-app omsebenzi"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Susa ukumisa"</string>
+ <!-- no translation found for work_scheduler_button_content_description (917340740986764967) -->
+ <skip />
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Hlunga"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Yehlulekile: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Isikhala esiyimfihlo"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 8a805c3..8bd25dd 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -58,16 +58,16 @@
<attr name="materialColorSecondaryFixedDim" format="color" />
<attr name="materialColorOnErrorContainer" format="color" />
<attr name="materialColorOnSecondaryFixed" format="color" />
- <attr name="materialColorOnSurfaceInverse" format="color" />
+ <attr name="materialColorInverseOnSurface" format="color" />
<attr name="materialColorTertiaryFixedDim" format="color" />
<attr name="materialColorOnTertiaryFixed" format="color" />
<attr name="materialColorPrimaryFixedDim" format="color" />
<attr name="materialColorSecondaryContainer" format="color" />
<attr name="materialColorErrorContainer" format="color" />
<attr name="materialColorOnPrimaryFixed" format="color" />
- <attr name="materialColorPrimaryInverse" format="color" />
+ <attr name="materialColorInversePrimary" format="color" />
<attr name="materialColorSecondaryFixed" format="color" />
- <attr name="materialColorSurfaceInverse" format="color" />
+ <attr name="materialColorInverseSurface" format="color" />
<attr name="materialColorSurfaceVariant" format="color" />
<attr name="materialColorTertiaryContainer" format="color" />
<attr name="materialColorTertiaryFixed" format="color" />
@@ -215,6 +215,8 @@
<attr name="minDeviceHeightPx" format="float"/>
<attr name="numRowsNew" format="integer"/>
<attr name="dbFile" />
+ <attr name="defaultLayoutId"/>
+ <attr name="demoModeLayoutId"/>
</declare-styleable>
<declare-styleable name="GridDisplayOption">
@@ -310,6 +312,10 @@
<attr name="rowCountSpecsId" format="reference" />
<!-- defaults to allAppsCellSpecsId, if not specified -->
<attr name="allAppsCellSpecsTwoPanelId" format="reference" />
+ <!-- defaults to false, if not specified -->
+ <attr name="isFixedLandscape" format="boolean" />
+ <!-- defaults to false, if not specified -->
+ <attr name="isOldGrid" format="boolean" />
<!-- By default all categories are enabled -->
<attr name="deviceCategory" format="integer">
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d1dde3f..61d99d7 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -155,6 +155,7 @@
<!-- Floating action button inside work tab to toggle work profile -->
<dimen name="work_fab_height">56dp</dimen>
<dimen name="work_fab_radius">16dp</dimen>
+ <dimen name="work_fab_elevation">6dp</dimen>
<dimen name="work_fab_icon_size">24dp</dimen>
<dimen name="work_fab_icon_vertical_margin">16dp</dimen>
<dimen name="work_fab_icon_start_margin_expanded">4dp</dimen>
@@ -228,6 +229,10 @@
<!-- Bottom margin for the search and recommended widgets container with work profile -->
<dimen name="search_and_recommended_widgets_container_small_bottom_margin">10dp</dimen>
+ <dimen name="widget_header_focus_ring_width">3dp</dimen>
+ <dimen name="widget_focus_ring_corner_radius">28dp</dimen>
+ <dimen name="widget_header_background_border">5dp</dimen>
+
<dimen name="widget_list_top_bottom_corner_radius">28dp</dimen>
<dimen name="widget_list_content_corner_radius">4dp</dimen>
<!-- Button that expands the widget apps list in the widget picker. -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f5af339..c280307 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -47,6 +47,8 @@
<!-- Title for an option to open a new window for a given app -->
<string name="new_window_option_taskbar">New Window</string>
+ <!-- Title for an option to manage open windows for a given app -->
+ <string name="manage_windows_option_taskbar">Manage Windows</string>
<!-- App pairs -->
<string name="save_app_pair">Save app pair</string>
@@ -317,6 +319,12 @@
<string name="allow_rotation_title">Allow home screen rotation</string>
<!-- Text explaining when the home screen will get rotated. [CHAR LIMIT=100] -->
<string name="allow_rotation_desc">When phone is rotated</string>
+
+ <!-- Title for Landscape Mode setting. [CHAR LIMIT=50] -->
+ <string name="landscape_mode_title">Landscape mode</string>
+ <!-- [CHAR LIMIT=100] -->
+ <string name="landscape_mode_desc">Set phone into landscape mode</string>
+
<!-- Title for Notification dots setting. Tapping this will link to the system Notifications settings screen where the user can turn off notification dots globally. [CHAR LIMIT=50] -->
<string name="notification_dots_title">Notification dots</string>
<!-- Text to indicate that the system notification dots setting is on [CHAR LIMIT=100] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 6d3579b..1c70d6c 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -41,16 +41,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorInverseOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorInversePrimary">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorInverseSurface">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
diff --git a/res/xml/backupscheme.xml b/res/xml/backupscheme.xml
index 58916a8..34b80b1 100644
--- a/res/xml/backupscheme.xml
+++ b/res/xml/backupscheme.xml
@@ -10,6 +10,7 @@
<include domain="database" path="launcher_4_by_4.db" />
<include domain="database" path="launcher_3_by_3.db" />
<include domain="database" path="launcher_2_by_2.db" />
+ <include domain="database" path="launcher_7_by_3.db" />
<include domain="sharedpref" path="com.android.launcher3.prefs.xml" />
<include domain="file" path="downgrade_schema.json" />
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 34cf56b..817cc40 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -20,6 +20,7 @@
import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
import static android.text.Layout.Alignment.ALIGN_NORMAL;
+import static com.android.launcher3.Flags.enableContrastTiles;
import static com.android.launcher3.Flags.enableCursorHoverStates;
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE;
@@ -39,6 +40,7 @@
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.icu.text.MessageFormat;
@@ -125,6 +127,8 @@
private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
+ private static final int APP_PILL_TITLE_PADDING = 8;
+
private float mScaleForReorderBounce = 1f;
private IntArray mBreakPointsIntArray;
@@ -722,6 +726,34 @@
}
}
+ /** Draws a background behind the App Title label when required. **/
+ public void drawAppContrastTile(Canvas canvas) {
+ RectF appTitleBounds;
+ Paint.FontMetrics fm = getPaint().getFontMetrics();
+ Rect tmpRect = new Rect();
+ getDrawingRect(tmpRect);
+ CharSequence text = getText();
+
+ float titleLength = (getPaint().measureText(text, 0, text.length())
+ + APP_PILL_TITLE_PADDING * 2);
+ titleLength = Math.min(titleLength, tmpRect.width());
+ appTitleBounds = new RectF((tmpRect.width() - titleLength) / 2.f - getCompoundPaddingLeft(),
+ 0, (tmpRect.width() + titleLength) / 2.f + getCompoundPaddingRight(),
+ (int) Math.ceil(fm.bottom - fm.top));
+
+
+ if (mIcon != null) {
+ Rect iconBounds = new Rect();
+ getIconBounds(iconBounds);
+ int textStart = iconBounds.bottom + getCompoundDrawablePadding();
+ appTitleBounds.offset(0, textStart);
+ }
+
+ canvas.drawRoundRect(appTitleBounds, appTitleBounds.height() / 2,
+ appTitleBounds.height() / 2,
+ PillColorProvider.getInstance(getContext()).getAppTitlePillPaint());
+ }
+
/** Draws a line under the app icon if this is representing a running app in Desktop Mode. */
protected void drawRunningAppIndicatorIfNecessary(Canvas canvas) {
if (mRunningAppState == RunningAppState.NOT_RUNNING || mDisplay != DISPLAY_TASKBAR) {
@@ -826,6 +858,11 @@
setPadding(getPaddingLeft(), (height - cellHeightPx) / 2, getPaddingRight(),
getPaddingBottom());
}
+ if (shouldDrawAppContrastTile()) {
+ setPadding(getPaddingLeft() + APP_PILL_TITLE_PADDING, getPaddingTop(),
+ getPaddingRight() + APP_PILL_TITLE_PADDING,
+ getPaddingBottom());
+ }
// Only apply two line for all_apps and device search only if necessary.
if (shouldUseTwoLine() && (mLastOriginalText != null)) {
int allowedVerticalSpace = height - getPaddingTop() - getPaddingBottom()
@@ -909,7 +946,9 @@
@Override
public void setTextColor(ColorStateList colors) {
- mTextColor = colors.getDefaultColor();
+ mTextColor = shouldDrawAppContrastTile() ? PillColorProvider.getInstance(
+ getContext()).getAppTitleTextPaint().getColor()
+ : colors.getDefaultColor();
mTextColorStateList = colors;
if (Float.compare(mTextAlpha, 1) == 0) {
super.setTextColor(colors);
@@ -926,6 +965,15 @@
&& info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION);
}
+ /**
+ * Whether or not an App title contrast tile should be drawn for this element.
+ **/
+ public boolean shouldDrawAppContrastTile() {
+ return mDisplay == DISPLAY_WORKSPACE && shouldTextBeVisible()
+ && PillColorProvider.getInstance(getContext()).isMatchaEnabled()
+ && enableContrastTiles();
+ }
+
public void setTextVisibility(boolean visible) {
setTextAlpha(visible ? 1 : 0);
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c25e8fb..09225e7 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -617,7 +617,7 @@
|| inv.inlineQsb[INDEX_TWO_PANEL_LANDSCAPE]
: inv.inlineQsb[INDEX_DEFAULT] || inv.inlineQsb[INDEX_LANDSCAPE])
&& hotseatQsbHeight > 0;
- isQsbInline = mIsScalableGrid && inv.inlineQsb[mTypeIndex] && canQsbInline;
+ isQsbInline = isQsbInline(inv);
areNavButtonsInline = isTaskbarPresent && !isGestureMode;
numShownHotseatIcons =
@@ -850,6 +850,24 @@
mDotRendererAllApps = createDotRenderer(context, allAppsIconSizePx, dotRendererCache);
}
+ /**
+ * Takes care of the logic that determines if we show a the QSB inline or not.
+ */
+ private boolean isQsbInline(InvariantDeviceProfile inv) {
+ // For foldable (two panel), we inline the qsb if we have the screen open and we are in
+ // either Landscape or Portrait. This cal also be disabled in the device_profile.xml
+ boolean twoPanelCanInline = inv.inlineQsb[INDEX_TWO_PANEL_PORTRAIT]
+ || inv.inlineQsb[INDEX_TWO_PANEL_LANDSCAPE];
+
+ // In tablets we inline in both orientations but only if we have enough space in the QSB
+ boolean tabletInlineQsb = inv.inlineQsb[INDEX_DEFAULT] || inv.inlineQsb[INDEX_LANDSCAPE];
+ boolean canQsbInline = isTwoPanels ? twoPanelCanInline : tabletInlineQsb;
+ canQsbInline = canQsbInline && hotseatQsbHeight > 0;
+
+ return (mIsScalableGrid && inv.inlineQsb[mTypeIndex] && canQsbInline)
+ || inv.isFixedLandscapeMode;
+ }
+
private static DotRenderer createDotRenderer(
@NonNull Context context, int size, @NonNull SparseArray<DotRenderer> cache) {
DotRenderer renderer = cache.get(size);
@@ -1815,7 +1833,8 @@
workspacePageIndicatorHeight - mWorkspacePageIndicatorOverlapWorkspace;
}
int paddingTop = workspaceTopPadding + (mIsScalableGrid ? 0 : edgeMarginPx);
- int paddingSide = desiredWorkspaceHorizontalMarginPx;
+ // On isFixedLandscapeMode on phones we already have padding because of the camera hole
+ int paddingSide = inv.isFixedLandscapeMode ? 0 : desiredWorkspaceHorizontalMarginPx;
padding.set(paddingSide, paddingTop, paddingSide, paddingBottom);
}
@@ -1903,7 +1922,7 @@
hotseatBarPadding.set(mHotseatBarWorkspaceSpacePx, paddingTop,
mInsets.right + mHotseatBarEdgePaddingPx, paddingBottom);
}
- } else if (isTaskbarPresent) {
+ } else if (isTaskbarPresent || inv.isFixedLandscapeMode) {
// Center the QSB vertically with hotseat
int hotseatBarBottomPadding = getHotseatBarBottomPadding();
int hotseatBarTopPadding =
@@ -1922,6 +1941,11 @@
}
startSpacing += getAdditionalQsbSpace();
+ if (inv.isFixedLandscapeMode) {
+ endSpacing += mInsets.right;
+ startSpacing += mInsets.left;
+ }
+
hotseatBarPadding.top = hotseatBarTopPadding;
hotseatBarPadding.bottom = hotseatBarBottomPadding;
boolean isRtl = Utilities.isRtl(context.getResources());
@@ -2517,7 +2541,8 @@
throw new IllegalArgumentException("Window bounds not set");
}
if (mTransposeLayoutWithOrientation == null) {
- mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds);
+ mTransposeLayoutWithOrientation =
+ !(mInfo.isTablet(mWindowBounds) || mInv.isFixedLandscapeMode);
}
if (mIsGestureMode == null) {
mIsGestureMode = mInfo.getNavigationMode().hasGestures;
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 27602af..b2ccba4 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -186,21 +186,21 @@
*/
public void adjustForBubbleBar(boolean isBubbleBarVisible) {
DeviceProfile dp = mActivity.getDeviceProfile();
-
+ float adjustedBorderSpace = dp.getHotseatAdjustedBorderSpaceForBubbleBar(getContext());
+ boolean shouldAdjustHotseat = isBubbleBarVisible
+ && Float.compare(adjustedBorderSpace, 0f) != 0;
ShortcutAndWidgetContainer icons = getShortcutsAndWidgets();
- AnimatorSet animatorSet = new AnimatorSet();
-
// update the translation provider for future layout passes of hotseat icons.
- if (isBubbleBarVisible) {
+ if (shouldAdjustHotseat) {
icons.setTranslationProvider(
cellX -> dp.getHotseatAdjustedTranslation(getContext(), cellX));
} else {
icons.setTranslationProvider(null);
}
-
+ AnimatorSet animatorSet = new AnimatorSet();
for (int i = 0; i < icons.getChildCount(); i++) {
View child = icons.getChildAt(i);
- float tx = isBubbleBarVisible ? dp.getHotseatAdjustedTranslation(getContext(), i) : 0;
+ float tx = shouldAdjustHotseat ? dp.getHotseatAdjustedTranslation(getContext(), i) : 0;
if (child instanceof Reorderable) {
MultiTranslateDelegate mtd = ((Reorderable) child).getTranslateDelegate();
animatorSet.play(
@@ -211,8 +211,8 @@
}
if (mQsb instanceof HorizontalInsettableView horizontalInsettableQsb) {
final float currentInsetFraction = horizontalInsettableQsb.getHorizontalInsets();
- final float targetInsetFraction =
- isBubbleBarVisible ? (float) dp.iconSizePx / dp.hotseatQsbWidth : 0;
+ final float targetInsetFraction = shouldAdjustHotseat
+ ? (float) dp.iconSizePx / dp.hotseatQsbWidth : 0;
ValueAnimator qsbAnimator =
ValueAnimator.ofFloat(currentInsetFraction, targetInsetFraction);
qsbAnimator.addUpdateListener(animation -> {
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index d8a898d..e18862a 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import static com.android.launcher3.LauncherPrefs.FIXED_LANDSCAPE_MODE;
import static com.android.launcher3.LauncherPrefs.GRID_NAME;
import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
@@ -86,7 +87,8 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef({TYPE_PHONE, TYPE_MULTI_DISPLAY, TYPE_TABLET})
- public @interface DeviceType {}
+ public @interface DeviceType {
+ }
public static final int TYPE_PHONE = 0;
public static final int TYPE_MULTI_DISPLAY = 1;
@@ -210,6 +212,13 @@
@XmlRes
public int allAppsCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
+
+ /**
+ * Fixed landscape mode is the landscape on the phones.
+ */
+ public boolean isFixedLandscapeMode = false;
+ private LauncherPrefChangeListener mLandscapeModePreferenceListener;
+
public String dbFile;
public int defaultLayoutId;
public int demoModeLayoutId;
@@ -225,7 +234,8 @@
private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
@VisibleForTesting
- public InvariantDeviceProfile() { }
+ public InvariantDeviceProfile() {
+ }
@TargetApi(23)
private InvariantDeviceProfile(Context context) {
@@ -243,6 +253,18 @@
onConfigChanged(displayContext);
}
});
+ if (Flags.oneGridSpecs()) {
+ mLandscapeModePreferenceListener = (String s) -> {
+ boolean newFixedLandscapeValue = FIXED_LANDSCAPE_MODE.get(context);
+ if (isFixedLandscapeMode != newFixedLandscapeValue) {
+ setFixedLandscape(context, newFixedLandscapeValue);
+ }
+ };
+ LauncherPrefs.INSTANCE.get(context).addListener(
+ mLandscapeModePreferenceListener,
+ FIXED_LANDSCAPE_MODE
+ );
+ }
}
/**
@@ -268,8 +290,13 @@
@DeviceType int defaultDeviceType = defaultInfo.getDeviceType();
DisplayOption defaultDisplayOption = invDistWeightedInterpolate(
defaultInfo,
- getPredefinedDeviceProfiles(context, gridName, defaultInfo,
- /*allowDisabledGrid=*/false),
+ getPredefinedDeviceProfiles(
+ context,
+ gridName,
+ defaultInfo,
+ /*allowDisabledGrid=*/false,
+ isFixedLandscapeMode
+ ),
defaultDeviceType);
Context displayContext = context.createDisplayContext(display);
@@ -277,8 +304,13 @@
@DeviceType int deviceType = myInfo.getDeviceType();
DisplayOption myDisplayOption = invDistWeightedInterpolate(
myInfo,
- getPredefinedDeviceProfiles(context, gridName, myInfo,
- /*allowDisabledGrid=*/false),
+ getPredefinedDeviceProfiles(
+ context,
+ gridName,
+ myInfo,
+ /*allowDisabledGrid=*/false,
+ isFixedLandscapeMode
+ ),
deviceType);
DisplayOption result = new DisplayOption(defaultDisplayOption.grid)
@@ -301,6 +333,11 @@
@Override
public void close() {
DisplayController.INSTANCE.executeIfCreated(dc -> dc.setPriorityListener(null));
+ if (mLandscapeModePreferenceListener != null) {
+ LauncherPrefs.INSTANCE.executeIfCreated(
+ lp -> lp.removeListener(mLandscapeModePreferenceListener, FIXED_LANDSCAPE_MODE)
+ );
+ }
}
public static String getCurrentGridName(Context context) {
@@ -308,10 +345,20 @@
}
private String initGrid(Context context, String gridName) {
+ if (!Flags.oneGridSpecs() && (isFixedLandscapeMode || FIXED_LANDSCAPE_MODE.get(context))) {
+ LauncherPrefs.get(context).put(FIXED_LANDSCAPE_MODE, false);
+ isFixedLandscapeMode = false;
+ }
+
Info displayInfo = DisplayController.INSTANCE.get(context).getInfo();
- List<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName,
- displayInfo, RestoreDbTask.isPending(context));
+ List<DisplayOption> allOptions = getPredefinedDeviceProfiles(
+ context,
+ gridName,
+ displayInfo,
+ RestoreDbTask.isPending(context),
+ FIXED_LANDSCAPE_MODE.get(context)
+ );
// Filter out options that don't have the same number of columns as the grid
DeviceGridState deviceGridState = new DeviceGridState(context);
@@ -320,8 +367,8 @@
DisplayOption displayOption =
invDistWeightedInterpolate(displayInfo, allOptionsFilteredByColCount.isEmpty()
- ? new ArrayList<>(allOptions)
- : new ArrayList<>(allOptionsFilteredByColCount),
+ ? new ArrayList<>(allOptions)
+ : new ArrayList<>(allOptionsFilteredByColCount),
displayInfo.getDeviceType());
initGrid(context, displayInfo, displayOption);
return displayOption.grid.name;
@@ -425,6 +472,9 @@
startAlignTaskbar = displayOption.startAlignTaskbar;
+ // Fixed Landscape mode
+ isFixedLandscapeMode = FIXED_LANDSCAPE_MODE.get(context) && Flags.oneGridSpecs();
+
// If the partner customization apk contains any grid overrides, apply them
// Supported overrides: numRows, numColumns, iconSize
applyPartnerDeviceProfileOverrides(context, metrics);
@@ -480,8 +530,12 @@
mChangeListeners.remove(listener);
}
- public void setCurrentGrid(Context context, String gridName) {
- LauncherPrefs.get(context).put(GRID_NAME, gridName);
+ /**
+ * Updates the current grid, this triggers a new IDP, reloads the database and triggers a grid
+ * migration.
+ */
+ public void setCurrentGrid(Context context, String newGridName) {
+ LauncherPrefs.get(context).put(GRID_NAME, newGridName);
MAIN_EXECUTOR.execute(() -> {
Trace.beginSection("InvariantDeviceProfile#setCurrentGrid");
onConfigChanged(context.getApplicationContext());
@@ -489,6 +543,24 @@
});
}
+ /**
+ * Updates the fixed landscape mode, this triggers a new IDP, reloads the database and triggers
+ * a grid migration.
+ */
+ public void setFixedLandscape(Context context, boolean isFixedLandscape) {
+ this.isFixedLandscapeMode = isFixedLandscape;
+ if (isFixedLandscape) {
+ // When in isFixedLandscape there should only be one default grid to choose from
+ MAIN_EXECUTOR.execute(() -> {
+ Trace.beginSection("InvariantDeviceProfile#setFixedLandscape");
+ onConfigChanged(context.getApplicationContext());
+ Trace.endSection();
+ });
+ } else {
+ setCurrentGrid(context, LauncherPrefs.get(context).get(GRID_NAME));
+ }
+ }
+
private Object[] toModelState() {
return new Object[]{
numColumns, numRows, numSearchContainerColumns, numDatabaseHotseatIcons,
@@ -510,8 +582,19 @@
}
}
- private static List<DisplayOption> getPredefinedDeviceProfiles(Context context, String gridName,
- Info displayInfo, boolean allowDisabledGrid) {
+ private static boolean firstGridFilter(GridOption gridOption, int deviceType,
+ boolean allowDisabledGrid, boolean isFixedLandscapeMode) {
+ return (gridOption.isEnabled(deviceType) || allowDisabledGrid)
+ && gridOption.filterByFlag(deviceType, isFixedLandscapeMode);
+ }
+
+ private static List<DisplayOption> getPredefinedDeviceProfiles(
+ Context context,
+ String gridName,
+ Info displayInfo,
+ boolean allowDisabledGrid,
+ boolean isFixedLandscapeMode
+ ) {
ArrayList<DisplayOption> profiles = new ArrayList<>();
try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
@@ -521,11 +604,10 @@
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG)
&& GridOption.TAG_NAME.equals(parser.getName())) {
-
GridOption gridOption = new GridOption(
context, Xml.asAttributeSet(parser), displayInfo);
- if ((gridOption.isEnabled(displayInfo.getDeviceType()) || allowDisabledGrid)
- && gridOption.filterByFlag(displayInfo.getDeviceType())) {
+ if (firstGridFilter(gridOption, displayInfo.getDeviceType(), allowDisabledGrid,
+ isFixedLandscapeMode)) {
final int displayDepth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG
|| parser.getDepth() > displayDepth)
@@ -542,7 +624,6 @@
} catch (IOException | XmlPullParserException e) {
throw new RuntimeException(e);
}
-
ArrayList<DisplayOption> filteredProfiles = new ArrayList<>();
if (!TextUtils.isEmpty(gridName)) {
for (DisplayOption option : profiles) {
@@ -650,7 +731,6 @@
* supported. Ej. 4x4 -> normal, 5x4 -> practical, etc.
* (Note: the name of the grid can be different for the same grid size depending of
* the values of the InvariantDeviceProfile)
- *
*/
public String getGridNameFromSize(Context context, Point size) {
return parseAllGridOptions(context).stream()
@@ -679,7 +759,7 @@
return parseAllDefinedGridOptions(context, displayInfo)
.stream()
.filter(go -> go.isEnabled(deviceType))
- .filter(go -> go.filterByFlag(deviceType))
+ .filter(go -> go.filterByFlag(deviceType, isFixedLandscapeMode))
.collect(Collectors.toList());
}
@@ -950,6 +1030,8 @@
private final int mAllAppsCellSpecsId;
private final int mAllAppsCellSpecsTwoPanelId;
private final int mRowCountSpecsId;
+ private final boolean mIsFixedLandscape;
+ private final boolean mIsOldGrid;
public GridOption(Context context, AttributeSet attrs, Info displayInfo) {
TypedArray a = context.obtainStyledAttributes(
@@ -966,18 +1048,20 @@
NumRows numR = getRowCount(resourceHelper, context, displayInfo);
numRows = numR.mNumRowsNew;
dbFile = numR.mDbFile;
+ defaultLayoutId = numR.mDefaultLayoutId;
+ demoModeLayoutId = numR.mDemoModeLayoutId;
} else {
numRows = a.getInt(R.styleable.GridDisplayOption_numRows, 0);
dbFile = a.getString(R.styleable.GridDisplayOption_dbFile);
+ defaultLayoutId = a.getResourceId(
+ R.styleable.GridDisplayOption_defaultLayoutId, 0);
+ demoModeLayoutId = a.getResourceId(
+ R.styleable.GridDisplayOption_demoModeLayoutId, defaultLayoutId);
}
numColumns = a.getInt(R.styleable.GridDisplayOption_numColumns, 0);
numSearchContainerColumns = a.getInt(
R.styleable.GridDisplayOption_numSearchContainerColumns, numColumns);
- defaultLayoutId = a.getResourceId(
- R.styleable.GridDisplayOption_defaultLayoutId, 0);
- demoModeLayoutId = a.getResourceId(
- R.styleable.GridDisplayOption_demoModeLayoutId, defaultLayoutId);
allAppsStyle = a.getResourceId(R.styleable.GridDisplayOption_allAppsStyle,
R.style.AllAppsStyleDefault);
@@ -1091,6 +1175,9 @@
mNumAllAppsRowsForCellHeightCalculation = numRows;
}
+ mIsFixedLandscape = a.getBoolean(R.styleable.GridDisplayOption_isFixedLandscape, false);
+ mIsOldGrid = a.getBoolean(R.styleable.GridDisplayOption_isOldGrid, false);
+
int inlineForRotation = a.getInt(R.styleable.GridDisplayOption_inlineQsb,
DONT_INLINE_QSB);
inlineQsb[INDEX_DEFAULT] =
@@ -1121,15 +1208,30 @@
}
}
- public boolean isNewGridOption() {
- return mRowCountSpecsId != INVALID_RESOURCE_HANDLE;
- }
-
- public boolean filterByFlag(int deviceType) {
+ /**
+ * Returns true if the grid option should be used given the flags that are toggled on/off.
+ */
+ public boolean filterByFlag(int deviceType, boolean isFixedLandscape) {
if (deviceType == TYPE_TABLET) {
return Flags.oneGridRotationHandling() == mIsDualGrid;
}
- return Flags.oneGridSpecs() == isNewGridOption();
+
+ // Here we return true if fixed landscape mode should be on.
+ if (mIsFixedLandscape || isFixedLandscape) {
+ return mIsFixedLandscape && isFixedLandscape && Flags.oneGridSpecs();
+ }
+
+ // Here we return true if we want to show the new grids.
+ if (mRowCountSpecsId != INVALID_RESOURCE_HANDLE) {
+ return Flags.oneGridSpecs();
+ }
+
+ // Here we return true if we want to show the old grids.
+ if (mIsOldGrid) {
+ return !Flags.oneGridSpecs();
+ }
+
+ return true;
}
}
@@ -1138,6 +1240,9 @@
final float mMinDeviceWidthPx;
final float mMinDeviceHeightPx;
final String mDbFile;
+ final int mDefaultLayoutId;
+ final int mDemoModeLayoutId;
+
NumRows(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NumRows);
@@ -1146,6 +1251,10 @@
mMinDeviceWidthPx = a.getFloat(R.styleable.NumRows_minDeviceWidthPx, 0);
mMinDeviceHeightPx = a.getFloat(R.styleable.NumRows_minDeviceHeightPx, 0);
mDbFile = a.getString(R.styleable.NumRows_dbFile);
+ mDefaultLayoutId = a.getResourceId(
+ R.styleable.NumRows_defaultLayoutId, 0);
+ mDemoModeLayoutId = a.getResourceId(
+ R.styleable.NumRows_demoModeLayoutId, mDefaultLayoutId);
a.recycle();
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 6446f7b..74dd971 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -279,6 +279,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -536,6 +537,7 @@
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
mWidgetPickerDataProvider = new WidgetPickerDataProvider();
+ PillColorProvider.getInstance(mWorkspace.getContext()).registerObserver();
boolean internalStateHandled = ACTIVITY_TRACKER.handleCreate(this);
if (internalStateHandled) {
@@ -761,7 +763,6 @@
if (!initDeviceProfile(mDeviceProfile.inv) && !mForceConfigUpdate) {
return;
}
-
dispatchDeviceProfileChanged();
reapplyUi();
mDragLayer.recreateControllers();
@@ -777,6 +778,20 @@
}
}
+ private void updateFixedLandscape() {
+ if (!com.android.launcher3.Flags.oneGridSpecs()) {
+ return;
+ }
+ // When the flag oneGridSpecs is on we want to disable ALLOW_ROTATION which is replaced
+ // by FIXED_LANDSCAPE_MODE, ALLOW_ROTATION will only be used on Tablets afterwards.
+ if (!getDeviceProfile().isTablet) {
+ LauncherPrefs.get(this).put(LauncherPrefs.ALLOW_ROTATION, false);
+ }
+ getRotationHelper().setFixedLandscape(
+ Objects.requireNonNull(mDeviceProfile.inv).isFixedLandscapeMode
+ );
+ }
+
public void onAssistantVisibilityChanged(float visibility) {
mHotseat.getQsb().setAlpha(1f - visibility);
}
@@ -805,6 +820,7 @@
mDeviceProfile.numShownHotseatIcons);
}
mModelWriter = mModel.getWriter(true, mCellPosMapper, this);
+ updateFixedLandscape();
return true;
}
@@ -1803,6 +1819,7 @@
// changes while launcher is still loading.
getRootView().getViewTreeObserver().removeOnPreDrawListener(mOnInitialBindListener);
mOverlayManager.onActivityDestroyed();
+ PillColorProvider.getInstance(mWorkspace.getContext()).unregisterObserver();
}
public LauncherAccessibilityDelegate getAccessibilityDelegate() {
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index 95c0ee8..a5b8168 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -24,6 +24,7 @@
public static final String LAUNCHER_4_BY_4_DB = "launcher_4_by_4.db";
public static final String LAUNCHER_3_BY_3_DB = "launcher_3_by_3.db";
public static final String LAUNCHER_2_BY_2_DB = "launcher_2_by_2.db";
+ public static final String LAUNCHER_8_BY_3_DB = "launcher_8_by_3.db";
public static final String BACKUP_DB = "backup.db";
public static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
public static final String MANAGED_USER_PREFERENCES_KEY =
@@ -43,7 +44,8 @@
LAUNCHER_5_BY_6_DB,
LAUNCHER_4_BY_4_DB,
LAUNCHER_3_BY_3_DB,
- LAUNCHER_2_BY_2_DB));
+ LAUNCHER_2_BY_2_DB,
+ LAUNCHER_8_BY_3_DB));
public static final List<String> OTHER_FILES = Collections.unmodifiableList(Arrays.asList(
BACKUP_DB,
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 5c03644..712c56c 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -26,6 +26,7 @@
import com.android.launcher3.pm.InstallSessionHelper
import com.android.launcher3.provider.RestoreDbTask
import com.android.launcher3.provider.RestoreDbTask.FIRST_LOAD_AFTER_RESTORE_KEY
+import com.android.launcher3.settings.SettingsActivity
import com.android.launcher3.states.RotationHelper
import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.MainThreadInitializedObject
@@ -133,6 +134,7 @@
nonRestorableItem(FIRST_LOAD_AFTER_RESTORE_KEY, false, EncryptionType.ENCRYPTED)
@JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
@JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
+
@JvmField
val GRID_NAME =
ConstantItem(
@@ -148,6 +150,9 @@
RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info)
}
+ @JvmField
+ val FIXED_LANDSCAPE_MODE = backedUpItem(SettingsActivity.FIXED_LANDSCAPE_MODE, false)
+
// Preferences for widget configurations
@JvmField
val RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
diff --git a/src/com/android/launcher3/PillColorPorovider.kt b/src/com/android/launcher3/PillColorPorovider.kt
new file mode 100644
index 0000000..347c5d6
--- /dev/null
+++ b/src/com/android/launcher3/PillColorPorovider.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3
+
+import android.content.Context
+import android.database.ContentObserver
+import android.graphics.Paint
+import android.net.Uri
+import android.provider.Settings
+import com.android.launcher3.util.Executors.ORDERED_BG_EXECUTOR
+
+class PillColorProvider private constructor(c: Context) {
+ private val context = c.applicationContext
+
+ private val matchaUri by lazy { Settings.Secure.getUriFor(MATCHA_SETTING) }
+ var appTitlePillPaint = Paint()
+ private set
+
+ var appTitleTextPaint = Paint()
+ private set
+
+ private var isMatchaEnabledInternal = 0
+
+ var isMatchaEnabled = isMatchaEnabledInternal != 0
+
+ private val pillColorObserver =
+ object : ContentObserver(ORDERED_BG_EXECUTOR.handler) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ if (uri == matchaUri) {
+ isMatchaEnabledInternal =
+ Settings.Secure.getInt(context.contentResolver, MATCHA_SETTING, 0)
+ isMatchaEnabled = isMatchaEnabledInternal != 0
+ }
+ }
+ }
+
+ fun registerObserver() {
+ context.contentResolver.registerContentObserver(matchaUri, false, pillColorObserver)
+ setup()
+ }
+
+ fun unregisterObserver() {
+ context.contentResolver.unregisterContentObserver(pillColorObserver)
+ }
+
+ fun setup() {
+ appTitlePillPaint.color =
+ context.resources.getColor(
+ R.color.material_color_surface_container_lowest,
+ context.theme,
+ )
+ appTitleTextPaint.color =
+ context.resources.getColor(R.color.material_color_on_surface, context.theme)
+ isMatchaEnabledInternal = Settings.Secure.getInt(context.contentResolver, MATCHA_SETTING, 0)
+ isMatchaEnabled = isMatchaEnabledInternal != 0
+ }
+
+ companion object {
+ private var INSTANCE: PillColorProvider? = null
+ private const val MATCHA_SETTING = "matcha_enable"
+
+ // TODO: Replace with a Dagger injection that is a singleton.
+ @JvmStatic
+ fun getInstance(context: Context): PillColorProvider {
+ if (INSTANCE == null) {
+ INSTANCE = PillColorProvider(context)
+ }
+ return INSTANCE!!
+ }
+ }
+}
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index b3cb948..f4d3146 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -303,10 +303,11 @@
.setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
.putExtra(Intent.EXTRA_USER, info.user);
context.startActivity(i);
- FileLog.d(TAG, "start uninstall activity " + cn.getPackageName());
+ FileLog.d(TAG, "start uninstall activity from drop target " + cn.getPackageName());
return cn;
} catch (URISyntaxException e) {
- Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info);
+ Log.e(TAG, "Failed to parse intent to start drop target uninstall activity for"
+ + " item=" + info);
return null;
}
}
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index 6168e41..ea5eb8f 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -73,8 +73,9 @@
|| alreadyAddedPromiseIcon) {
FileLog.d(LOG,
String.format(Locale.ENGLISH,
- "Removing PromiseIcon for package: %s, install reason: %d,"
- + " alreadyAddedPromiseIcon: %s",
+ "Removing unneeded PromiseIcon for package: %s"
+ + ", install reason: %d,"
+ + " alreadyAddedPromiseIcon: %s",
info.getAppPackageName(),
info.getInstallReason(),
alreadyAddedPromiseIcon
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 0e9c861..95dbf5f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import static com.android.launcher3.AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME;
import static com.android.launcher3.BubbleTextView.DISPLAY_FOLDER;
import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
@@ -1222,6 +1223,10 @@
}
protected void onPageBeginTransition() {
+ // Widget resize frame doesn't receive events to close when talkback is enabled. For that
+ // case, close it here.
+ AbstractFloatingView.closeOpenViews(mLauncher, false, TYPE_WIDGET_RESIZE_FRAME);
+
super.onPageBeginTransition();
updateChildrenLayersEnabled();
}
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 1b58987..c938482 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -28,6 +28,7 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE;
import static com.android.launcher3.views.RecyclerViewFastScroller.FastScrollerLocation.ALL_APPS_SCROLLER;
+import static com.android.window.flags.Flags.predictiveBackThreeButtonNav;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -1173,8 +1174,10 @@
super.dispatchDraw(canvas);
if (mNavBarScrimHeight > 0) {
- canvas.drawRect(0, getHeight() - mNavBarScrimHeight, getWidth(), getHeight(),
- mNavBarScrimPaint);
+ float left = (getWidth() - getWidth() / getScaleX()) / 2;
+ float top = getHeight() / 2f + (getHeight() / 2f - mNavBarScrimHeight) / getScaleY();
+ canvas.drawRect(left, top, getWidth() / getScaleX(),
+ top + mNavBarScrimHeight / getScaleY(), mNavBarScrimPaint);
}
}
@@ -1340,6 +1343,17 @@
invalidateHeader();
}
+ @Override
+ public void setScaleY(float scaleY) {
+ super.setScaleY(scaleY);
+ if (predictiveBackThreeButtonNav() && mNavBarScrimHeight > 0) {
+ // Call invalidate to prevent navbar scrim from scaling. The navbar scrim is drawn
+ // directly onto the canvas. To prevent it from being scaled with the canvas, there's a
+ // counter scale applied in dispatchDraw.
+ invalidate(20, getHeight() - mNavBarScrimHeight, getWidth(), getHeight());
+ }
+ }
+
/**
* Set {@link Animator.AnimatorListener} on {@link mAllAppsTransitionController} to observe
* animation of backing out of all apps search view to all apps view.
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index bd604eb..8554de5 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -43,9 +43,9 @@
import androidx.annotation.FloatRange;
import androidx.annotation.Nullable;
-import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
@@ -55,14 +55,16 @@
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.AllAppsSwipeController;
+import com.android.launcher3.util.MSDLPlayerWrapper;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.ScrollableLayoutManager;
import com.android.launcher3.util.Themes;
-import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.ScrimView;
+import com.google.android.msdl.data.model.MSDLToken;
+
/**
* Handles AllApps view transition.
* 1) Slides all apps view using direct manipulation
@@ -183,7 +185,7 @@
private MultiPropertyFactory<View> mAppsViewTranslationY;
private boolean mHasScaleEffect;
- private final VibratorWrapper mVibratorWrapper;
+ private final MSDLPlayerWrapper mMSDLPlayerWrapper;
public AllAppsTransitionController(Launcher l) {
mLauncher = l;
@@ -197,7 +199,7 @@
setShiftRange(dp.allAppsShiftRange);
mAllAppScale.value = 1;
mLauncher.addOnDeviceProfileChangeListener(this);
- mVibratorWrapper = VibratorWrapper.INSTANCE.get(mLauncher.getApplicationContext());
+ mMSDLPlayerWrapper = MSDLPlayerWrapper.INSTANCE.get(mLauncher.getApplicationContext());
}
public float getShiftRange() {
@@ -277,10 +279,9 @@
return;
}
- float deceleratedProgress = Interpolators.BACK_GESTURE.getInterpolation(backProgress);
float scaleProgress = ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE
+ (1 - ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE)
- * (1 - deceleratedProgress);
+ * (1 - backProgress);
mAllAppScale.updateValue(scaleProgress);
}
@@ -370,8 +371,16 @@
setAlphas(toState, config, builder);
// This controls both haptics for tapping on QSB and going to all apps.
if (ALL_APPS.equals(toState) && mLauncher.isInState(NORMAL)) {
- mLauncher.getAppsView().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ if (Flags.msdlFeedback()) {
+ if (config.isUserControlled()) {
+ mMSDLPlayerWrapper.playToken(MSDLToken.SWIPE_THRESHOLD_INDICATOR);
+ } else {
+ mMSDLPlayerWrapper.playToken(MSDLToken.TAP_HIGH_EMPHASIS);
+ }
+ } else {
+ mLauncher.getAppsView().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ }
}
}
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index 21dce14..609edd2 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -45,6 +45,7 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
@@ -220,6 +221,7 @@
* when animation is not running.
*/
public void reset() {
+ Trace.beginSection("PrivateProfileManager#reset");
// Ensure the state of the header view is what it should be before animating.
updateView();
getMainRecyclerView().setChildAttachedConsumer(null);
@@ -239,6 +241,7 @@
executeLock();
}
addPrivateSpaceDecorator(updatedState);
+ Trace.endSection();
}
/** Returns whether or not Private Space Settings Page is available. */
@@ -293,31 +296,12 @@
}
}
- @Override
public void setQuietMode(boolean enable) {
- UI_HELPER_EXECUTOR.post(() ->
- mUserCache.getUserProfiles()
- .stream()
- .filter(getUserMatcher())
- .findFirst()
- .ifPresent(userHandle -> setQuietModeSafely(enable, userHandle)));
+ setQuietMode(enable, mAllApps.mActivityContext);
mReadyToAnimate = true;
}
/**
- * Sets Quiet Mode for Private Profile.
- * If {@link SecurityException} is thrown, prompts the user to set this launcher as HOME app.
- */
- private void setQuietModeSafely(boolean enable, UserHandle userHandle) {
- try {
- mUserManager.requestQuietModeEnabled(enable, userHandle);
- } catch (SecurityException ex) {
- ApiWrapper.INSTANCE.get(mAllApps.mActivityContext)
- .assignDefaultHomeRole(mAllApps.mActivityContext);
- }
- }
-
- /**
* Expand the private space after the app list has been added and updated from
* {@link AlphabeticalAppsList#onAppsUpdated()}
*/
@@ -331,7 +315,9 @@
/** Collapses the private space before the app list has been updated. */
void executeLock() {
+ Trace.beginSection("PrivateProfileManager#executeLock");
MAIN_EXECUTOR.execute(() -> updatePrivateStateAnimator(false));
+ Trace.endSection();
}
void setAnimationRunning(boolean isAnimationRunning) {
@@ -378,6 +364,7 @@
if (mPSHeader == null) {
return;
}
+ Trace.beginSection("PrivateProfileManager#updateView");
Log.d(TAG, "bindPrivateSpaceHeaderViewElements: " + "Updating view with state: "
+ getCurrentState());
mPSHeader.setAlpha(1);
@@ -436,6 +423,7 @@
}
}
mPSHeader.invalidate();
+ Trace.endSection();
}
/** Sets the enablement of the profile when header or button is clicked. */
@@ -840,6 +828,7 @@
ActivityAllAppsContainerView<?>.AdapterHolder mainAdapterHolder = mAllApps.mAH.get(MAIN);
List<BaseAllAppsAdapter.AdapterItem> adapterItems =
mainAdapterHolder.mAppsList.getAdapterItems();
+ Trace.beginSection("PrivateProfileManager#expandPrivateSpace");
if (Flags.enablePrivateSpace() && Flags.privateSpaceAnimation()
&& mAllApps.isPersonalTab()) {
// Animate the text and settings icon.
@@ -849,6 +838,7 @@
getPsHeaderHeight(), deviceProfile.allAppsCellHeightPx);
updatePrivateStateAnimator(true);
}
+ Trace.endSection();
}
private void exitSearchAndExpand() {
diff --git a/src/com/android/launcher3/allapps/UserProfileManager.java b/src/com/android/launcher3/allapps/UserProfileManager.java
index 93b6b29..765c29c 100644
--- a/src/com/android/launcher3/allapps/UserProfileManager.java
+++ b/src/com/android/launcher3/allapps/UserProfileManager.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
@@ -26,6 +27,7 @@
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.util.ApiWrapper;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -69,14 +71,26 @@
}
/** Sets quiet mode as enabled/disabled for the profile type. */
- protected void setQuietMode(boolean enabled) {
+ protected void setQuietMode(boolean enabled, Context context) {
UI_HELPER_EXECUTOR.post(() ->
mUserCache.getUserProfiles()
.stream()
.filter(getUserMatcher())
.findFirst()
.ifPresent(userHandle ->
- mUserManager.requestQuietModeEnabled(enabled, userHandle)));
+ setQuietModeSafely(enabled, userHandle, context)));
+ }
+
+ /**
+ * Sets Quiet Mode for Private Profile.
+ * If {@link SecurityException} is thrown, prompts the user to set this launcher as HOME app.
+ */
+ private void setQuietModeSafely(boolean enable, UserHandle userHandle, Context context) {
+ try {
+ mUserManager.requestQuietModeEnabled(enable, userHandle);
+ } catch (SecurityException ex) {
+ ApiWrapper.INSTANCE.get(context).assignDefaultHomeRole(context);
+ }
}
/** Sets current state for the profile type. */
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index 3d0c1d0..6ebab5a 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -74,7 +74,7 @@
*/
public void setWorkProfileEnabled(boolean enabled) {
updateCurrentState(STATE_TRANSITION);
- setQuietMode(!enabled);
+ setQuietMode(!enabled, mAllApps.mActivityContext);
}
@Override
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 0e20f75..fb486f7 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -24,6 +24,7 @@
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.DaggerSingletonTracker;
+import com.android.launcher3.util.MSDLPlayerWrapper;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PluginManagerWrapper;
import com.android.launcher3.util.ScreenOnTracker;
@@ -56,6 +57,7 @@
PackageManagerHelper getPackageManagerHelper();
PluginManagerWrapper getPluginManagerWrapper();
VibratorWrapper getVibratorWrapper();
+ MSDLPlayerWrapper getMSDLPlayerWrapper();
/** Builder for LauncherBaseAppComponent. */
interface Builder {
diff --git a/src/com/android/launcher3/dagger/LauncherComponentProvider.kt b/src/com/android/launcher3/dagger/LauncherComponentProvider.kt
new file mode 100644
index 0000000..5015e54
--- /dev/null
+++ b/src/com/android/launcher3/dagger/LauncherComponentProvider.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dagger
+
+import android.content.Context
+import android.view.LayoutInflater
+import com.android.launcher3.LauncherApplication
+
+/**
+ * Utility class to extract LauncherAppComponent from a context.
+ *
+ * If the context doesn't provide LauncherAppComponent by default, it creates a new one and
+ * associate it with that context
+ */
+object LauncherComponentProvider {
+
+ @JvmStatic
+ fun get(c: Context): LauncherAppComponent {
+ val app = c.applicationContext
+ if (app is LauncherApplication) return app.appComponent
+
+ val inflater = LayoutInflater.from(app)
+ val existingFilter = inflater.filter
+ if (existingFilter is Holder) return existingFilter.component
+
+ // Create a new component
+ return Holder(
+ DaggerLauncherAppComponent.builder().appContext(app).build()
+ as LauncherAppComponent,
+ existingFilter,
+ )
+ .apply { inflater.filter = this }
+ .component
+ }
+
+ private data class Holder(
+ val component: LauncherAppComponent,
+ private val filter: LayoutInflater.Filter?,
+ ) : LayoutInflater.Filter {
+
+ override fun onLoadClass(clazz: Class<*>?) = filter?.onLoadClass(clazz) ?: true
+ }
+}
diff --git a/src/com/android/launcher3/dragndrop/LauncherDragController.java b/src/com/android/launcher3/dragndrop/LauncherDragController.java
index 29fc613..4aa3673 100644
--- a/src/com/android/launcher3/dragndrop/LauncherDragController.java
+++ b/src/com/android/launcher3/dragndrop/LauncherDragController.java
@@ -119,6 +119,9 @@
initialDragViewScale,
dragViewScaleOnDrop,
scalePx);
+ // During a drag, we don't want to expose the descendendants of drag view to a11y users,
+ // since those decendents are not a valid position in the workspace.
+ dragView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
dragView.setItemInfo(dragInfo);
mDragObject.dragComplete = false;
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 94c36c0..3000b25 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -16,6 +16,8 @@
package com.android.launcher3.graphics;
+import static android.content.res.Configuration.UI_MODE_NIGHT_NO;
+import static android.content.res.Configuration.UI_MODE_NIGHT_YES;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
@@ -25,6 +27,7 @@
import android.app.WallpaperColors;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
+import android.content.res.Configuration;
import android.database.Cursor;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
@@ -84,6 +87,7 @@
private static final String KEY_COLORS = "wallpaper_colors";
private static final String KEY_COLOR_RESOURCE_IDS = "color_resource_ids";
private static final String KEY_COLOR_VALUES = "color_values";
+ private static final String KEY_DARK_MODE = "use_dark_mode";
private Context mContext;
private final IBinder mHostToken;
@@ -95,6 +99,7 @@
private final Display mDisplay;
private final WallpaperColors mWallpaperColors;
private SparseIntArray mPreviewColorOverride;
+ @Nullable private Boolean mDarkMode;
private final RunnableList mLifeCycleTracker;
private final SurfaceControlViewHost mSurfaceControlViewHost;
@@ -235,6 +240,8 @@
}
private void updateColorOverrides(Bundle bundle) {
+ mDarkMode =
+ bundle.containsKey(KEY_DARK_MODE) ? bundle.getBoolean(KEY_DARK_MODE) : null;
int[] ids = bundle.getIntArray(KEY_COLOR_RESOURCE_IDS);
int[] colors = bundle.getIntArray(KEY_COLOR_VALUES);
if (ids != null && colors != null) {
@@ -253,6 +260,18 @@
*/
private Context getPreviewContext() {
Context context = mContext.createDisplayContext(mDisplay);
+ if (mDarkMode != null) {
+ Configuration configuration = new Configuration(
+ context.getResources().getConfiguration());
+ if (mDarkMode) {
+ configuration.uiMode &= ~UI_MODE_NIGHT_NO;
+ configuration.uiMode |= UI_MODE_NIGHT_YES;
+ } else {
+ configuration.uiMode &= ~UI_MODE_NIGHT_YES;
+ configuration.uiMode |= UI_MODE_NIGHT_NO;
+ }
+ context = context.createConfigurationContext(configuration);
+ }
if (Flags.newCustomizationPickerUi()) {
if (mPreviewColorOverride != null) {
LocalColorExtractor.newInstance(context)
diff --git a/src/com/android/launcher3/model/GridSizeMigrationDBController.java b/src/com/android/launcher3/model/GridSizeMigrationDBController.java
index 2d6be7e..bfa00bd 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationDBController.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationDBController.java
@@ -17,13 +17,14 @@
package com.android.launcher3.model;
import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle;
-import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
+import static com.android.launcher3.Flags.oneGridSpecs;
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.LauncherSettings.Favorites.TMP_TABLE;
import static com.android.launcher3.Utilities.SHOULD_SHOW_FIRST_PAGE_WIDGET;
import static com.android.launcher3.model.LoaderTask.SMARTSPACE_ON_HOME_SCREEN;
import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
+import static com.android.launcher3.provider.LauncherDbUtils.shiftTableByXCells;
import android.content.ComponentName;
import android.content.ContentValues;
@@ -118,18 +119,33 @@
@NonNull DeviceGridState srcDeviceState,
@NonNull DeviceGridState destDeviceState,
@NonNull DatabaseHelper target,
- @NonNull SQLiteDatabase source) {
+ @NonNull SQLiteDatabase source,
+ boolean isDestNewDb) {
if (!needsToMigrate(srcDeviceState, destDeviceState)) {
return true;
}
- if (LauncherPrefs.get(context).get(IS_FIRST_LOAD_AFTER_RESTORE)
+ if (isDestNewDb
&& srcDeviceState.getColumns().equals(destDeviceState.getColumns())
&& srcDeviceState.getRows() < destDeviceState.getRows()) {
// Only use this strategy when comparing the previous grid to the new grid and the
// columns are the same and the destination has more rows
copyTable(source, TABLE_NAME, target.getWritableDatabase(), TABLE_NAME, context);
+
+ if (oneGridSpecs()) {
+ DbReader destReader = new DbReader(
+ target.getWritableDatabase(), TABLE_NAME, context);
+ boolean shouldShiftCells = shouldShiftCells(destReader, srcDeviceState.getRows());
+ if (shouldShiftCells) {
+ shiftTableByXCells(
+ target.getWritableDatabase(),
+ (destDeviceState.getRows() - srcDeviceState.getRows()),
+ TABLE_NAME);
+ }
+ }
+
+ // Save current configuration, so that the migration does not run again.
destDeviceState.writeToPrefs(context);
return true;
}
@@ -427,17 +443,22 @@
}
}
- static void copyCurrentGridToNewGrid(
- @NonNull Context context,
- @NonNull DeviceGridState destDeviceState,
- @NonNull DatabaseHelper target,
- @NonNull SQLiteDatabase source) {
- // Only use this strategy when comparing the previous grid to the new grid and the
- // columns are the same and the destination has more rows
- copyTable(source, TABLE_NAME, target.getWritableDatabase(), TABLE_NAME, context);
- destDeviceState.writeToPrefs(context);
+ private static boolean shouldShiftCells(final DbReader destReader, final int srcGridRowCount) {
+ List<DbEntry> workspaceItems = destReader.loadAllWorkspaceEntries();
+ int firstPageItemsRowPosSum = workspaceItems.stream()
+ .filter(entry -> entry.screenId == 0)
+ .mapToInt(entry -> entry.cellY).sum();
+ int firstPageWorkspaceItemsCount = (int) workspaceItems.stream()
+ .filter(entry -> entry.screenId == 0).count();
+ if (firstPageWorkspaceItemsCount == 0) {
+ return false;
+ }
+ float srcGridMidPoint = srcGridRowCount / 2f;
+ float firstPageItemPosAvg = (float) firstPageItemsRowPosSum / firstPageWorkspaceItemsCount;
+ return (firstPageItemPosAvg >= srcGridMidPoint);
}
+
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public static class DbReader {
diff --git a/src/com/android/launcher3/model/GridSizeMigrationLogic.kt b/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
index 07316ef..3f52d8a 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
+++ b/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
@@ -21,15 +21,20 @@
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.launcher3.Flags
+import com.android.launcher3.Flags.oneGridSpecs
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.get
import com.android.launcher3.LauncherPrefs.Companion.getPrefs
import com.android.launcher3.LauncherSettings
+import com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME
+import com.android.launcher3.LauncherSettings.Favorites.TMP_TABLE
import com.android.launcher3.Utilities
import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.model.GridSizeMigrationDBController.DbReader
-import com.android.launcher3.provider.LauncherDbUtils
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction
+import com.android.launcher3.provider.LauncherDbUtils.copyTable
+import com.android.launcher3.provider.LauncherDbUtils.dropTable
+import com.android.launcher3.provider.LauncherDbUtils.shiftTableByXCells
import com.android.launcher3.util.CellAndSpan
import com.android.launcher3.util.GridOccupancy
import com.android.launcher3.util.IntArray
@@ -46,6 +51,7 @@
destDeviceState: DeviceGridState,
target: DatabaseHelper,
source: SQLiteDatabase,
+ isDestNewDb: Boolean,
) {
if (!GridSizeMigrationDBController.needsToMigrate(srcDeviceState, destDeviceState)) {
return
@@ -57,28 +63,31 @@
// This is a special case where if the grid is the same amount of columns but a larger
// amount of rows we simply copy over the source grid to the destination grid, rather
// than undergoing the general grid migration.
- if (shouldMigrateToStrictlyTallerGrid(isFirstLoad, srcDeviceState, destDeviceState)) {
- GridSizeMigrationDBController.copyCurrentGridToNewGrid(
- context,
- destDeviceState,
- target,
- source,
- )
+ if (shouldMigrateToStrictlyTallerGrid(isDestNewDb, srcDeviceState, destDeviceState)) {
+ copyTable(source, TABLE_NAME, target.writableDatabase, TABLE_NAME, context)
+ if (oneGridSpecs()) {
+ val destReader = DbReader(target.writableDatabase, TABLE_NAME, context)
+ val shouldShiftCells = shouldShiftCells(destReader, srcDeviceState.rows)
+ if (shouldShiftCells) {
+ shiftTableByXCells(
+ target.writableDatabase,
+ (destDeviceState.rows - srcDeviceState.rows),
+ TABLE_NAME,
+ )
+ }
+ }
+ // Save current configuration, so that the migration does not run again.
+ destDeviceState.writeToPrefs(context)
return
}
- LauncherDbUtils.copyTable(
- source,
- LauncherSettings.Favorites.TABLE_NAME,
- target.writableDatabase,
- LauncherSettings.Favorites.TMP_TABLE,
- context,
- )
+
+ copyTable(source, TABLE_NAME, target.writableDatabase, TMP_TABLE, context)
val migrationStartTime = System.currentTimeMillis()
try {
SQLiteTransaction(target.writableDatabase).use { t ->
- val srcReader = DbReader(t.db, LauncherSettings.Favorites.TMP_TABLE, context)
- val destReader = DbReader(t.db, LauncherSettings.Favorites.TABLE_NAME, context)
+ val srcReader = DbReader(t.db, TMP_TABLE, context)
+ val destReader = DbReader(t.db, TABLE_NAME, context)
val targetSize = Point(destDeviceState.columns, destDeviceState.rows)
@@ -94,7 +103,7 @@
// Migrate workspace.
migrateWorkspace(srcReader, destReader, target, targetSize, idsInUse)
- LauncherDbUtils.dropTable(t.db, LauncherSettings.Favorites.TMP_TABLE)
+ dropTable(t.db, TMP_TABLE)
t.commit()
}
} catch (e: Exception) {
@@ -111,6 +120,19 @@
}
}
+ private fun shouldShiftCells(destReader: DbReader, srcGridRowCount: Int): Boolean {
+ val workspaceItems = destReader.loadAllWorkspaceEntries()
+ val firstPageItemsRowPosSum =
+ workspaceItems.sumOf { entry -> if (entry.screenId == 0) entry.cellY else 0 }
+ val firstPageWorkspaceItemsCount = workspaceItems.count { entry -> entry.screenId == 0 }
+ if (firstPageWorkspaceItemsCount == 0) {
+ return false
+ }
+ val srcGridMidPoint = srcGridRowCount / 2f
+ val firstPageItemPosAvg = firstPageItemsRowPosSum / firstPageWorkspaceItemsCount.toFloat()
+ return (firstPageItemPosAvg >= srcGridMidPoint)
+ }
+
/** Handles hotseat migration. */
@VisibleForTesting
fun migrateHotseat(
@@ -335,11 +357,11 @@
/** Only migrate the grid in this manner if the target grid is taller and not wider. */
private fun shouldMigrateToStrictlyTallerGrid(
- isFirstLoad: Boolean,
+ isDestNewDb: Boolean,
srcDeviceState: DeviceGridState,
destDeviceState: DeviceGridState,
): Boolean {
- return isFirstLoad &&
+ return isDestNewDb &&
srcDeviceState.columns == destDeviceState.columns &&
srcDeviceState.rows < destDeviceState.rows
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index a830c96..83eace8 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -246,7 +246,7 @@
TraceHelper.INSTANCE.beginSection(TAG);
LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
mIsRestoreFromBackup =
- (Boolean) LauncherPrefs.get(mApp.getContext()).get(IS_FIRST_LOAD_AFTER_RESTORE);
+ LauncherPrefs.get(mApp.getContext()).get(IS_FIRST_LOAD_AFTER_RESTORE);
LauncherRestoreEventLogger restoreEventLogger = null;
if (enableLauncherBrMetricsFixed()) {
restoreEventLogger = LauncherRestoreEventLogger.Companion
@@ -266,21 +266,21 @@
sanitizeFolders(mItemsDeleted);
sanitizeAppPairs();
sanitizeWidgetsShortcutsAndPackages();
- logASplit("sanitizeData");
+ logASplit("sanitizeData finished");
}
verifyNotStopped();
mLauncherBinder.bindWorkspace(true /* incrementBindId */, /* isBindSync= */ false);
- logASplit("bindWorkspace");
+ logASplit("bindWorkspace finished");
mModelDelegate.workspaceLoadComplete();
// Notify the installer packages of packages with active installs on the first screen.
sendFirstScreenActiveInstallsBroadcast();
- logASplit("sendFirstScreenBroadcast");
+ logASplit("sendFirstScreenBroadcast finished");
// Take a break
waitForIdle();
- logASplit("step 1 complete");
+ logASplit("step 1 loading workspace complete");
verifyNotStopped();
// second step
@@ -291,11 +291,11 @@
} finally {
Trace.endSection();
}
- logASplit("loadAllApps");
+ logASplit("loadAllApps finished");
verifyNotStopped();
mLauncherBinder.bindAllApps();
- logASplit("bindAllApps");
+ logASplit("bindAllApps finished");
verifyNotStopped();
IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
@@ -303,28 +303,28 @@
updateHandler.updateIcons(allActivityList,
LauncherActivityCachingLogic.INSTANCE,
mApp.getModel()::onPackageIconsUpdated);
- logASplit("update icon cache");
+ logASplit("update AllApps icon cache finished");
verifyNotStopped();
- logASplit("save shortcuts in icon cache");
+ logASplit("saving all shortcuts in icon cache");
updateHandler.updateIcons(allShortcuts, CacheableShortcutCachingLogic.INSTANCE,
mApp.getModel()::onPackageIconsUpdated);
// Take a break
waitForIdle();
- logASplit("step 2 complete");
+ logASplit("step 2 loading AllApps complete");
verifyNotStopped();
// third step
List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
- logASplit("loadDeepShortcuts");
+ logASplit("loadDeepShortcuts finished");
verifyNotStopped();
mLauncherBinder.bindDeepShortcuts();
- logASplit("bindDeepShortcuts");
+ logASplit("bindDeepShortcuts finished");
verifyNotStopped();
- logASplit("save deep shortcuts in icon cache");
+ logASplit("saving deep shortcuts in icon cache");
updateHandler.updateIcons(
convertShortcutsToCacheableShortcuts(allDeepShortcuts, allActivityList),
CacheableShortcutCachingLogic.INSTANCE,
@@ -332,7 +332,7 @@
// Take a break
waitForIdle();
- logASplit("step 3 complete");
+ logASplit("step 3 loading all shortcuts complete");
verifyNotStopped();
// fourth step
@@ -345,11 +345,11 @@
widgetsModel.updateWidgetFilters(mWidgetsFilterDataProvider);
}
List<CachedObject> allWidgetsList = widgetsModel.update(mApp, /*packageUser=*/null);
- logASplit("load widgets");
+ logASplit("load widgets finished");
verifyNotStopped();
mLauncherBinder.bindWidgets();
- logASplit("bindWidgets");
+ logASplit("bindWidgets finished");
verifyNotStopped();
LauncherPrefs prefs = LauncherPrefs.get(mApp.getContext());
@@ -357,7 +357,7 @@
mLauncherBinder.bindSmartspaceWidget();
// Turn off pref.
prefs.putSync(SHOULD_SHOW_SMARTSPACE.to(false));
- logASplit("bindSmartspaceWidget");
+ logASplit("bindSmartspaceWidget finished");
verifyNotStopped();
} else if (!enableSmartspaceAsAWidget() && WIDGET_ON_FIRST_SCREEN
&& !prefs.get(LauncherPrefs.SHOULD_SHOW_SMARTSPACE)) {
@@ -365,10 +365,10 @@
prefs.putSync(SHOULD_SHOW_SMARTSPACE.to(true));
}
+ logASplit("saving all widgets in icon cache");
updateHandler.updateIcons(allWidgetsList,
CachedObjectCachingLogic.INSTANCE,
mApp.getModel()::onWidgetLabelsUpdated);
- logASplit("save widgets in icon cache");
// fifth step
loadFolderNames();
@@ -414,7 +414,7 @@
} finally {
Trace.endSection();
}
- logASplit("loadWorkspace");
+ logASplit("loadWorkspace finished");
mBgDataModel.isFirstPagePinnedItemEnabled = FeatureFlags.QSB_ON_FIRST_SCREEN
&& (!enableSmartspaceRemovalToggle() || LauncherPrefs.getPrefs(
@@ -440,7 +440,7 @@
} else {
dbController.tryMigrateDB(restoreEventLogger);
}
- Log.d(TAG, "loadWorkspace: loading default favorites");
+ Log.d(TAG, "loadWorkspace: loading default favorites if necessary");
dbController.loadDefaultFavoritesIfNecessary();
synchronized (mBgDataModel) {
@@ -453,7 +453,7 @@
mInstallingPkgsCached = installingPkgs;
}
installingPkgs.forEach(mApp.getIconCache()::updateSessionCache);
- FileLog.d(TAG, "loadWorkspace: Packages with active install sessions: "
+ FileLog.d(TAG, "loadWorkspace: Packages with active install/update sessions: "
+ installingPkgs.keySet().stream().map(info -> info.mPackageName).toList());
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
@@ -478,8 +478,12 @@
widgetInflater, mPmHelper, iconRequestInfos, unlockedUsers,
allDeepShortcuts);
- while (!mStopped && c.moveToNext()) {
- itemProcessor.processItem();
+ if (mStopped) {
+ Log.w(TAG, "loadWorkspaceImpl: Loader stopped, skipping item processing");
+ } else {
+ while (!mStopped && c.moveToNext()) {
+ itemProcessor.processItem();
+ }
}
tryLoadWorkspaceIconsInBulk(iconRequestInfos);
} finally {
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index 094798b..6ff8547 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -89,6 +89,7 @@
import java.io.File;
import java.io.InputStream;
import java.io.StringReader;
+import java.util.List;
/**
* Utility class which maintains an instance of Launcher database and provides utility methods
@@ -368,6 +369,13 @@
InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
DatabaseHelper oldHelper = mOpenHelper;
+
+ // We save the existing db's before creating the destination db helper so we know what logic
+ // to run in grid migration based on if that grid already existed before migration or not.
+ List<String> existingDBs = LauncherFiles.GRID_DB_FILES.stream()
+ .filter(dbName -> mContext.getDatabasePath(dbName).exists())
+ .toList();
+
mOpenHelper = (mContext instanceof SandboxContext) ? oldHelper
: createDatabaseHelper(true, new DeviceGridState(idp).getDbFile());
try {
@@ -376,9 +384,11 @@
// This is the state we want to migrate to that is given by the idp
DeviceGridState destDeviceState = new DeviceGridState(idp);
+ boolean isDestNewDb = !existingDBs.contains(destDeviceState.getDbFile());
+
GridSizeMigrationLogic gridSizeMigrationLogic = new GridSizeMigrationLogic();
gridSizeMigrationLogic.migrateGrid(mContext, srcDeviceState, destDeviceState,
- mOpenHelper, oldHelper.getWritableDatabase());
+ mOpenHelper, oldHelper.getWritableDatabase(), isDestNewDb);
} catch (Exception e) {
resetLauncherDb(restoreEventLogger);
throw new Exception("Failed to migrate grid", e);
@@ -441,6 +451,13 @@
return false;
}
DatabaseHelper oldHelper = mOpenHelper;
+
+ // We save the existing db's before creating the destination db helper so we know what logic
+ // to run in grid migration based on if that grid already existed before migration or not.
+ List<String> existingDBs = LauncherFiles.GRID_DB_FILES.stream()
+ .filter(dbName -> mContext.getDatabasePath(dbName).exists())
+ .toList();
+
mOpenHelper = (mContext instanceof SandboxContext) ? oldHelper
: createDatabaseHelper(true /* forMigration */, targetDbName);
try {
@@ -448,8 +465,11 @@
DeviceGridState srcDeviceState = new DeviceGridState(mContext);
// This is the state we want to migrate to that is given by the idp
DeviceGridState destDeviceState = new DeviceGridState(idp);
+
+ boolean isDestNewDb = !existingDBs.contains(destDeviceState.getDbFile());
+
return GridSizeMigrationDBController.migrateGridIfNeeded(mContext, srcDeviceState,
- destDeviceState, mOpenHelper, oldHelper.getWritableDatabase());
+ destDeviceState, mOpenHelper, oldHelper.getWritableDatabase(), isDestNewDb);
} catch (Exception e) {
FileLog.e(TAG, "Failed to migrate grid", e);
return false;
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
index 856c294..b9c928c 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -33,7 +33,6 @@
import androidx.annotation.WorkerThread;
import com.android.launcher3.Flags;
-import com.android.launcher3.logging.FileLog;
import com.android.launcher3.util.PackageUserKey;
import java.lang.ref.WeakReference;
@@ -79,7 +78,7 @@
}
SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId, helper, callback);
if (sessionInfo != null) {
- FileLog.d(TAG, "onCreated: Install session created for"
+ Log.d(TAG, "onCreated: Install session created for"
+ " appPackageName=" + sessionInfo.getAppPackageName()
+ ", sessionId=" + sessionInfo.getSessionId()
+ ", appIcon=" + sessionInfo.getAppIcon()
@@ -111,7 +110,7 @@
activeSessions.remove(sessionId);
if (key != null && key.mPackageName != null) {
- FileLog.d(TAG, "onFinished: active install session finished for"
+ Log.d(TAG, "onFinished: active install session finished for"
+ " appPackageName=" + key.mPackageName
+ ", sessionId=" + sessionId
+ ", success=" + success);
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.kt b/src/com/android/launcher3/provider/LauncherDbUtils.kt
index 3c68e46..6f1d0dd 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.kt
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.kt
@@ -131,6 +131,11 @@
}
}
+ @JvmStatic
+ fun shiftTableByXCells(db: SQLiteDatabase, x: Int, toTable: String) {
+ db.run { execSQL("UPDATE $toTable SET cellY = cellY + $x") }
+ }
+
/**
* Migrates the legacy shortcuts to deep shortcuts pinned under Launcher. Removes any invalid
* shortcut or any shortcut which requires some permission to launch
diff --git a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
index 82229f8..e4c50f0 100644
--- a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
+++ b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
@@ -18,18 +18,23 @@
import android.content.Context
import android.util.Log
+import android.view.InflateException
+import androidx.annotation.VisibleForTesting
+import androidx.annotation.VisibleForTesting.Companion.PROTECTED
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.RecycledViewPool
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.android.launcher3.BubbleTextView
import com.android.launcher3.BuildConfig
import com.android.launcher3.allapps.BaseAllAppsAdapter
+import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.util.CancellableTask
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.util.Executors.VIEW_PREINFLATION_EXECUTOR
import com.android.launcher3.util.Themes
import com.android.launcher3.views.ActivityContext
import com.android.launcher3.views.ActivityContext.ActivityContextDelegate
+import java.lang.IllegalStateException
const val PREINFLATE_ICONS_ROW_COUNT = 4
const val EXTRA_ICONS_COUNT = 2
@@ -39,10 +44,11 @@
* [RecyclerView]. The view inflation will happen on background thread and inflated [ViewHolder]s
* will be added to [RecycledViewPool] on main thread.
*/
-class AllAppsRecyclerViewPool<T> : RecycledViewPool() {
+class AllAppsRecyclerViewPool<T> : RecycledViewPool() where T : Context, T : ActivityContext {
var hasWorkProfile = false
- private var mCancellableTask: CancellableTask<List<ViewHolder>>? = null
+ @VisibleForTesting(otherwise = PROTECTED)
+ var mCancellableTask: CancellableTask<List<ViewHolder>>? = null
companion object {
private const val TAG = "AllAppsRecyclerViewPool"
@@ -53,7 +59,7 @@
/**
* Preinflate app icons. If all apps RV cannot be scrolled down, we don't need to preinflate.
*/
- fun <T> preInflateAllAppsViewHolders(context: T) where T : Context, T : ActivityContext {
+ fun preInflateAllAppsViewHolders(context: T) {
val appsView = context.appsView ?: return
val activeRv: RecyclerView = appsView.activeRecyclerView ?: return
val preInflateCount = getPreinflateCount(context)
@@ -97,36 +103,65 @@
override fun getLayoutManager(): RecyclerView.LayoutManager? = null
}
+ preInflateAllAppsViewHolders(
+ adapter,
+ BaseAllAppsAdapter.VIEW_TYPE_ICON,
+ activeRv,
+ preInflateCount,
+ ) {
+ getPreinflateCount(context)
+ }
+ }
+
+ @VisibleForTesting(otherwise = PROTECTED)
+ fun preInflateAllAppsViewHolders(
+ adapter: RecyclerView.Adapter<*>,
+ viewType: Int,
+ activeRv: RecyclerView,
+ preInflationCount: Int,
+ preInflationCountProvider: () -> Int,
+ ) {
+ if (preInflationCount <= 0) {
+ return
+ }
mCancellableTask?.cancel()
var task: CancellableTask<List<ViewHolder>>? = null
task =
CancellableTask(
{
val list: ArrayList<ViewHolder> = ArrayList()
- for (i in 0 until preInflateCount) {
+ for (i in 0 until preInflationCount) {
if (task?.canceled == true) {
break
}
// If activeRv's layout manager has been reset to null on main thread, skip
// the preinflation as we cannot generate correct LayoutParams
if (activeRv.layoutManager == null) {
+ list.clear()
break
}
- list.add(
- adapter.createViewHolder(activeRv, BaseAllAppsAdapter.VIEW_TYPE_ICON)
- )
+ try {
+ list.add(adapter.createViewHolder(activeRv, viewType))
+ } catch (e: InflateException) {
+ list.clear()
+ // It's still possible for UI thread to set activeRv's layout manager to
+ // null and we should break the loop and cancel the preinflation.
+ break
+ }
}
list
},
MAIN_EXECUTOR,
{ viewHolders ->
- for (i in 0 until minOf(viewHolders.size, getPreinflateCount(context))) {
+ // Run preInflationCountProvider again as the needed VH might have changed
+ val newPreInflationCount = preInflationCountProvider.invoke()
+ for (i in 0 until minOf(viewHolders.size, newPreInflationCount)) {
putRecycledView(viewHolders[i])
}
},
)
mCancellableTask = task
- VIEW_PREINFLATION_EXECUTOR.submit(mCancellableTask)
+ VIEW_PREINFLATION_EXECUTOR.execute(mCancellableTask)
}
/**
@@ -143,10 +178,11 @@
* app icons plus [EXTRA_ICONS_COUNT] is the magic minimal count of app icons to preinflate to
* suffice fast scrolling.
*
- * Note that we need to preinfate extra app icons in size of one all apps pages, so that opening
- * all apps don't need to inflate app icons.
+ * Note that if [FeatureFlags.ALL_APPS_GONE_VISIBILITY] is enabled, we need to preinfate extra
+ * app icons in size of one all apps pages, so that opening all apps don't need to inflate app
+ * icons.
*/
- fun <T> getPreinflateCount(context: T): Int where T : Context, T : ActivityContext {
+ fun getPreinflateCount(context: T): Int {
var targetPreinflateCount =
PREINFLATE_ICONS_ROW_COUNT * context.deviceProfile.numShownAllAppsColumns +
EXTRA_ICONS_COUNT
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index bd9298b..5068b48 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -22,10 +22,12 @@
import static com.android.launcher3.BuildConfig.IS_DEBUG_DEVICE;
import static com.android.launcher3.BuildConfig.IS_STUDIO_BUILD;
+import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY;
import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
import android.app.Activity;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
@@ -51,6 +53,7 @@
import com.android.launcher3.BuildConfig;
import com.android.launcher3.Flags;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherFiles;
import com.android.launcher3.R;
import com.android.launcher3.states.RotationHelper;
@@ -66,6 +69,8 @@
@VisibleForTesting
static final String DEVELOPER_OPTIONS_KEY = "pref_developer_options";
+ public static final String FIXED_LANDSCAPE_MODE = "pref_fixed_landscape_mode";
+
private static final String NOTIFICATION_DOTS_PREFERENCE_KEY = "pref_icon_badging";
public static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args";
@@ -236,7 +241,7 @@
/**
* Finds the parent preference screen for the given target key.
*
- * @param parent the parent preference screen
+ * @param parent the parent preference screen
* @param targetKey the key of the preference to find
* @return the parent preference screen that contains the target preference
*/
@@ -286,13 +291,14 @@
* will remove that preference from the list.
*/
protected boolean initPreference(Preference preference) {
+ DisplayController.Info info = DisplayController.INSTANCE.get(getContext()).getInfo();
switch (preference.getKey()) {
case NOTIFICATION_DOTS_PREFERENCE_KEY:
return BuildConfig.NOTIFICATION_DOTS_ENABLED;
-
case ALLOW_ROTATION_PREFERENCE_KEY:
- DisplayController.Info info =
- DisplayController.INSTANCE.get(getContext()).getInfo();
+ if (Flags.oneGridSpecs()) {
+ return false;
+ }
if (info.isTablet(info.realBounds)) {
// Launcher supports rotation by default. No need to show this setting.
return false;
@@ -300,14 +306,32 @@
// Initialize the UI once
preference.setDefaultValue(RotationHelper.getAllowRotationDefaultValue(info));
return true;
-
case DEVELOPER_OPTIONS_KEY:
if (IS_STUDIO_BUILD) {
preference.setOrder(0);
}
return mDeveloperOptionsEnabled;
+ case FIXED_LANDSCAPE_MODE:
+ if (!Flags.oneGridSpecs()
+ // adding this condition until fixing b/378972567
+ || InvariantDeviceProfile.INSTANCE.get(getContext()).deviceType
+ == TYPE_MULTI_DISPLAY) {
+ return false;
+ }
+ // When the setting changes rotate the screen accordingly to showcase the result
+ // of the setting
+ preference.setOnPreferenceChangeListener(
+ (pref, newValue) -> {
+ getActivity().setRequestedOrientation(
+ (boolean) newValue
+ ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+ : ActivityInfo.SCREEN_ORIENTATION_USER
+ );
+ return true;
+ }
+ );
+ return !info.isTablet(info.realBounds);
}
-
return true;
}
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 4c9da5d..7d7ccd3 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -18,6 +18,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE;
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
import static com.android.launcher3.LauncherPrefs.ALLOW_ROTATION;
@@ -63,6 +64,8 @@
public static final int REQUEST_ROTATE = 1;
public static final int REQUEST_LOCK = 2;
+ private boolean mIsFixedLandscape = false;
+
@NonNull
private final BaseActivity mActivity;
private final Handler mRequestOrientationHandler;
@@ -163,6 +166,18 @@
notifyChange();
}
+ public boolean isFixedLandscape() {
+ return mIsFixedLandscape;
+ }
+
+ /**
+ * If fixedLandscape is true then the Launcher become landscape until set false..
+ */
+ public void setFixedLandscape(boolean fixedLandscape) {
+ mIsFixedLandscape = fixedLandscape;
+ notifyChange();
+ }
+
// Used by tests only.
public void forceAllowRotationForTesting(boolean allowRotation) {
if (mDestroyed) return;
@@ -203,6 +218,8 @@
SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED;
} else if (mCurrentStateRequest == REQUEST_LOCK) {
activityFlags = SCREEN_ORIENTATION_LOCKED;
+ } else if (mIsFixedLandscape) {
+ activityFlags = SCREEN_ORIENTATION_USER_LANDSCAPE;
} else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE
|| mHomeRotationEnabled || mForceAllowRotationForTesting) {
activityFlags = SCREEN_ORIENTATION_UNSPECIFIED;
diff --git a/src/com/android/launcher3/util/DaggerSingletonObject.java b/src/com/android/launcher3/util/DaggerSingletonObject.java
index febe6af..a245761 100644
--- a/src/com/android/launcher3/util/DaggerSingletonObject.java
+++ b/src/com/android/launcher3/util/DaggerSingletonObject.java
@@ -18,8 +18,8 @@
import android.content.Context;
-import com.android.launcher3.LauncherApplication;
import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherComponentProvider;
import java.util.function.Function;
@@ -37,8 +37,6 @@
}
public T get(Context context) {
- LauncherAppComponent component =
- ((LauncherApplication) context.getApplicationContext()).getAppComponent();
- return mFunction.apply(component);
+ return mFunction.apply(LauncherComponentProvider.get(context));
}
}
diff --git a/src/com/android/launcher3/util/MSDLPlayerWrapper.java b/src/com/android/launcher3/util/MSDLPlayerWrapper.java
new file mode 100644
index 0000000..1e53ac1
--- /dev/null
+++ b/src/com/android/launcher3/util/MSDLPlayerWrapper.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.content.Context;
+import android.os.Vibrator;
+
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.dagger.LauncherBaseAppComponent;
+
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.InteractionProperties;
+import com.google.android.msdl.domain.MSDLPlayer;
+
+import javax.inject.Inject;
+
+/**
+ * Wrapper around {@link com.google.android.msdl.domain.MSDLPlayer} to perform MSDL feedback.
+ */
+@LauncherAppSingleton
+public class MSDLPlayerWrapper {
+
+ public static final DaggerSingletonObject<MSDLPlayerWrapper> INSTANCE =
+ new DaggerSingletonObject<>(LauncherBaseAppComponent::getMSDLPlayerWrapper);
+
+ /** Internal player */
+ private final MSDLPlayer mMSDLPlayer;
+
+ @Inject
+ public MSDLPlayerWrapper(@ApplicationContext Context context) {
+ Vibrator vibrator = context.getSystemService(Vibrator.class);
+ mMSDLPlayer = MSDLPlayer.Companion.createPlayer(vibrator, UI_HELPER_EXECUTOR, null);
+ }
+
+ /** Perform MSDL feedback for a token with interaction properties */
+ public void playToken(MSDLToken token, InteractionProperties properties) {
+ mMSDLPlayer.playToken(token, properties);
+ }
+
+ /** Perform MSDL feedback for a token without properties */
+ public void playToken(MSDLToken token) {
+ mMSDLPlayer.playToken(token, null);
+ }
+}
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index f457e4e..44a7c6f 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -73,7 +73,27 @@
*/
public static final int STAGE_TYPE_SIDE = 1;
- @IntDef({STAGE_TYPE_UNDEFINED, STAGE_TYPE_MAIN, STAGE_TYPE_SIDE})
+ /**
+ * Position independent stage identifier for a given Stage
+ */
+ public static final int STAGE_TYPE_A = 2;
+ /**
+ * Position independent stage identifier for a given Stage
+ */
+ public static final int STAGE_TYPE_B = 3;
+ /**
+ * Position independent stage identifier for a given Stage
+ */
+ public static final int STAGE_TYPE_C = 4;
+
+ @IntDef({
+ STAGE_TYPE_UNDEFINED,
+ STAGE_TYPE_MAIN,
+ STAGE_TYPE_SIDE,
+ STAGE_TYPE_A,
+ STAGE_TYPE_B,
+ STAGE_TYPE_C
+ })
public @interface StageType {}
///////////////////////////////////
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index 85aad89..65d02d0 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -298,8 +298,7 @@
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public void onBackProgressed(BackEvent backEvent) {
final float progress = backEvent.getProgress();
- float deceleratedProgress = Interpolators.BACK_GESTURE.getInterpolation(progress);
- mSwipeToDismissProgress.updateValue(deceleratedProgress);
+ mSwipeToDismissProgress.updateValue(progress);
}
/**
diff --git a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
index ef66ffe..392d9a7 100644
--- a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
+++ b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
@@ -102,6 +102,9 @@
@Override
public void onDraw(Canvas canvas) {
+ if (shouldDrawAppContrastTile()) {
+ drawAppContrastTile(canvas);
+ }
// If text is transparent or shadow alpha is 0, don't draw any shadow
if (skipDoubleShadow()) {
super.onDraw(canvas);
diff --git a/src/com/android/launcher3/views/StickyHeaderLayout.java b/src/com/android/launcher3/views/StickyHeaderLayout.java
index 090251f..4142e1f 100644
--- a/src/com/android/launcher3/views/StickyHeaderLayout.java
+++ b/src/com/android/launcher3/views/StickyHeaderLayout.java
@@ -120,7 +120,19 @@
}
private float getCurrentScroll() {
- return mScrollOffset + (mCurrentEmptySpaceView == null ? 0 : mCurrentEmptySpaceView.getY());
+ float scroll;
+ if (mCurrentRecyclerView.getVisibility() != VISIBLE) {
+ // When no list is displayed, assume no scroll.
+ scroll = 0f;
+ } else if (mCurrentEmptySpaceView != null) {
+ // Otherwise use empty space view as reference to position.
+ scroll = mCurrentEmptySpaceView.getY();
+ } else {
+ // If there is no empty space view, but the list is visible, we are scrolled away
+ // completely, so assume all non-sticky children should also be scrolled away.
+ scroll = -mHeaderHeight;
+ }
+ return mScrollOffset + scroll;
}
@Override
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 1c0d94c..fda5175 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_ADD_BUTTON_TAP;
+import static com.android.window.flags.Flags.predictiveBackThreeButtonNav;
import android.content.Context;
import android.graphics.Canvas;
@@ -128,6 +129,17 @@
}
@Override
+ public void setScaleY(float scaleY) {
+ super.setScaleY(scaleY);
+ if (predictiveBackThreeButtonNav() && mNavBarScrimHeight > 0) {
+ // Call invalidate to prevent navbar scrim from scaling. The navbar scrim is drawn
+ // directly onto the canvas. To prevent it from being scaled with the canvas, there's a
+ // counter scale applied in dispatchDraw.
+ invalidate();
+ }
+ }
+
+ @Override
public final void onClick(View v) {
WidgetCell wc;
if (v instanceof WidgetCell view) {
@@ -318,8 +330,10 @@
super.dispatchDraw(canvas);
if (mNavBarScrimHeight > 0) {
- canvas.drawRect(0, getHeight() - mNavBarScrimHeight, getWidth(), getHeight(),
- mNavBarScrimPaint);
+ float left = (getWidth() - getWidth() / getScaleX()) / 2;
+ float top = getHeight() / 2f + (getHeight() / 2f - mNavBarScrimHeight) / getScaleY();
+ canvas.drawRect(left, top, getWidth() / getScaleX(),
+ top + mNavBarScrimHeight / getScaleY(), mNavBarScrimPaint);
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 2f64ab1..150806a 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -295,8 +295,11 @@
}
private void attachScrollbarToRecyclerView(WidgetsRecyclerView recyclerView) {
- recyclerView.bindFastScrollbar(mFastScroller, WIDGET_SCROLLER);
if (mCurrentWidgetsRecyclerView != recyclerView) {
+ // Bind scrollbar if changing the recycler view. If widgets list updates, since
+ // scrollbar is already attached to the recycler view, it will automatically adjust as
+ // needed with recycler view's onScrollListener.
+ recyclerView.bindFastScrollbar(mFastScroller, WIDGET_SCROLLER);
// Only reset the scroll position & expanded apps if the currently shown recycler view
// has been updated.
reset();
@@ -605,9 +608,12 @@
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.setVisibility(GONE);
mAdapters.get(getCurrentAdapterHolderType()).mWidgetsRecyclerView.setVisibility(
VISIBLE);
- // Visibility of recommended widgets, recycler views and headers are handled in methods
- // below.
- post(this::onRecommendedWidgetsBound);
+ if (mRecommendedWidgetsCount > 0) {
+ // Display recommendations immediately, if present, so that other parts of sticky
+ // header (e.g. personal / work tabs) don't flash in interim.
+ mWidgetRecommendationsContainer.setVisibility(VISIBLE);
+ }
+ // Visibility of recycler views and headers are handled in methods below.
onWidgetsBound();
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index 3c67538..74a9a5c 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -103,7 +103,7 @@
.equals(mWidgetsContentVisiblePackageUserKey);
@Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
@Nullable private RecyclerView mRecyclerView;
- @Nullable private PackageUserKey mPendingClickHeader;
+ @Nullable private PackageUserKey mHeaderPositionToMaintain;
@Px private int mMaxHorizontalSpan;
private boolean mShowOnlyDefaultList = true;
@@ -215,7 +215,7 @@
// Get the current top of the header with the matching key before adjusting the visible
// entries.
OptionalInt previousPositionForPackageUserKey =
- getPositionForPackageUserKey(mPendingClickHeader);
+ getPositionForPackageUserKey(mHeaderPositionToMaintain);
OptionalInt topForPackageUserKey =
getOffsetForPosition(previousPositionForPackageUserKey);
@@ -247,13 +247,15 @@
mVisibleEntries.addAll(newVisibleEntries);
diffResult.dispatchUpdatesTo(this);
- if (mPendingClickHeader != null) {
+ if (mHeaderPositionToMaintain != null && mRecyclerView != null) {
// Get the position for the clicked header after adjusting the visible entries. The
// position may have changed if another header had previously been expanded.
OptionalInt positionForPackageUserKey =
- getPositionForPackageUserKey(mPendingClickHeader);
- scrollToPositionAndMaintainOffset(positionForPackageUserKey, topForPackageUserKey);
- mPendingClickHeader = null;
+ getPositionForPackageUserKey(mHeaderPositionToMaintain);
+ // Post scroll updates to be applied after diff updates.
+ mRecyclerView.post(() -> scrollToPositionAndMaintainOffset(positionForPackageUserKey,
+ topForPackageUserKey));
+ mHeaderPositionToMaintain = null;
}
}
@@ -384,7 +386,7 @@
// Store the header that was clicked so that its position will be maintained the next time
// we update the entries.
- mPendingClickHeader = packageUserKey;
+ mHeaderPositionToMaintain = packageUserKey;
updateVisibleEntries();
@@ -470,6 +472,16 @@
*/
public void useExpandedList() {
mShowOnlyDefaultList = false;
+ if (mWidgetsContentVisiblePackageUserKey != null) {
+ // Maintain selected header for the next update that expands the list.
+ mHeaderPositionToMaintain = mWidgetsContentVisiblePackageUserKey;
+ } else if (mVisibleEntries.size() > 2) {
+ // Maintain last visible header shown above expand button since there was no selected
+ // header.
+ mHeaderPositionToMaintain = PackageUserKey.fromPackageItemInfo(
+ mVisibleEntries.get(mVisibleEntries.size() - 2).mPkgItem);
+ }
+
}
/** Comparator for sorting WidgetListRowEntry based on package title. */
diff --git a/tests/Android.bp b/tests/Android.bp
index 35a2275..e4fecc5 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -63,7 +63,6 @@
"src/com/android/launcher3/dragging/TaplDragTest.java",
"src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java",
"src/com/android/launcher3/ui/TaplTestsLauncher3Test.java",
- "src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java",
"src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java",
],
}
@@ -173,6 +172,7 @@
"multivalentTests/src/**/*.java",
"multivalentTests/src/**/*.kt",
"src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
+ "src/com/android/launcher3/ui/BaseLauncherTaplTest.java",
"tapl/com/android/launcher3/tapl/*.java",
"tapl/com/android/launcher3/tapl/*.kt",
],
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 68e493d..1ddd453 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -183,7 +183,6 @@
</activity>
<activity-alias android:name="Activity2"
android:label="TestActivity2"
- android:icon="@drawable/test_icon"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
@@ -193,7 +192,6 @@
</activity-alias>
<activity-alias android:name="Activity3"
android:label="TestActivity3"
- android:icon="@drawable/test_icon"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
@@ -203,7 +201,6 @@
</activity-alias>
<activity-alias android:name="Activity4"
android:label="TestActivity4"
- android:icon="@drawable/test_icon"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
@@ -213,7 +210,6 @@
</activity-alias>
<activity-alias android:name="Activity5"
android:label="TestActivity5"
- android:icon="@drawable/test_icon"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
@@ -223,7 +219,6 @@
</activity-alias>
<activity-alias android:name="Activity6"
android:label="TestActivity6"
- android:icon="@drawable/test_icon"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
@@ -233,7 +228,6 @@
</activity-alias>
<activity-alias android:name="Activity7"
android:label="TestActivity7"
- android:icon="@drawable/test_icon"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
@@ -243,7 +237,6 @@
</activity-alias>
<activity-alias android:name="Activity8"
android:label="TestActivity8"
- android:icon="@drawable/test_icon"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
@@ -253,7 +246,6 @@
</activity-alias>
<activity-alias android:name="Activity9" android:exported="true"
android:label="TestActivity9"
- android:icon="@drawable/test_icon"
android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -262,7 +254,6 @@
</activity-alias>
<activity-alias android:name="Activity10" android:exported="true"
android:label="TestActivity10"
- android:icon="@drawable/test_icon"
android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -271,7 +262,6 @@
</activity-alias>
<activity-alias android:name="Activity11" android:exported="true"
android:label="TestActivity11"
- android:icon="@drawable/test_icon"
android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -280,7 +270,6 @@
</activity-alias>
<activity-alias android:name="Activity12" android:exported="true"
android:label="TestActivity12"
- android:icon="@drawable/test_icon"
android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -289,7 +278,6 @@
</activity-alias>
<activity-alias android:name="Activity13" android:exported="true"
android:label="TestActivity13"
- android:icon="@drawable/test_icon"
android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -298,7 +286,6 @@
</activity-alias>
<activity-alias android:name="Activity14" android:exported="true"
android:label="TestActivity14"
- android:icon="@drawable/test_icon"
android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -376,7 +363,7 @@
</activity>
<activity android:name="com.android.launcher3.testcomponent.ImeTestActivity"
android:label="ImeTestActivity"
- android:icon="@drawable/test_icon"
+ android:icon="@drawable/test_theme_icon"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/tests/assets/ReorderWidgets/push_reorder_case b/tests/assets/ReorderWidgets/push_reorder_case
index 1eacfae..73b67d0 100644
--- a/tests/assets/ReorderWidgets/push_reorder_case
+++ b/tests/assets/ReorderWidgets/push_reorder_case
@@ -39,6 +39,6 @@
board: 6x5
xxxxxx
bbbb--
---m---
---aaa-
---ddd-
\ No newline at end of file
+--maaa
+--ddd-
+------
\ No newline at end of file
diff --git a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index 55a028b..f8794f8 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -126,6 +126,7 @@
deviceSpec: DeviceSpec,
isGestureMode: Boolean = true,
isVerticalBar: Boolean = false,
+ isFixedLandscape: Boolean = false,
) {
val (naturalX, naturalY) = deviceSpec.naturalSize
val windowsBounds = phoneWindowsBounds(deviceSpec, isGestureMode, naturalX, naturalY)
@@ -138,6 +139,7 @@
rotation = if (isVerticalBar) Surface.ROTATION_90 else Surface.ROTATION_0,
isGestureMode,
densityDpi = deviceSpec.densityDpi,
+ isFixedLandscape = isFixedLandscape,
)
}
@@ -166,6 +168,7 @@
isLandscape: Boolean = false,
isGestureMode: Boolean = true,
isFolded: Boolean = false,
+ isFixedLandscape: Boolean = false,
) {
val (unfoldedNaturalX, unfoldedNaturalY) = deviceSpecUnfolded.naturalSize
val unfoldedWindowsBounds =
@@ -192,6 +195,7 @@
rotation = if (isLandscape) Surface.ROTATION_90 else Surface.ROTATION_0,
isGestureMode = isGestureMode,
densityDpi = deviceSpecFolded.densityDpi,
+ isFixedLandscape = isFixedLandscape,
)
} else {
initializeCommonVars(
@@ -200,6 +204,7 @@
rotation = if (isLandscape) Surface.ROTATION_0 else Surface.ROTATION_90,
isGestureMode = isGestureMode,
densityDpi = deviceSpecUnfolded.densityDpi,
+ isFixedLandscape = isFixedLandscape,
)
}
}
@@ -274,6 +279,7 @@
rotation: Int,
isGestureMode: Boolean = true,
densityDpi: Int,
+ isFixedLandscape: Boolean = false,
) {
setFlagsRule.setFlags(true, Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
LauncherPrefs.get(testContext).put(LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE, true)
@@ -307,6 +313,7 @@
whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING)).thenReturn(false)
whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(true)
+ whenever(launcherPrefs.get(LauncherPrefs.FIXED_LANDSCAPE_MODE)).thenReturn(isFixedLandscape)
whenever(launcherPrefs.get(LauncherPrefs.HOTSEAT_COUNT)).thenReturn(-1)
whenever(launcherPrefs.get(LauncherPrefs.DEVICE_TYPE)).thenReturn(-1)
whenever(launcherPrefs.get(LauncherPrefs.WORKSPACE_SIZE)).thenReturn("")
diff --git a/tests/multivalentTests/src/com/android/launcher3/dagger/LauncherComponentProviderTest.kt b/tests/multivalentTests/src/com/android/launcher3/dagger/LauncherComponentProviderTest.kt
new file mode 100644
index 0000000..9255877
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/dagger/LauncherComponentProviderTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dagger
+
+import android.content.Context
+import android.content.ContextWrapper
+import android.view.ContextThemeWrapper
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.R
+import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
+import com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNotSame
+import org.junit.Assert.assertSame
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class LauncherComponentProviderTest {
+
+ val app: Context = ApplicationProvider.getApplicationContext()
+
+ @Test
+ fun `returns same component as Launcher application`() {
+ val c = SandboxModelContext()
+ assertSame(c.appComponent, LauncherComponentProvider.get(c))
+ assertNotSame(LauncherComponentProvider.get(c), LauncherComponentProvider.get(app))
+ }
+
+ @Test
+ fun `returns same component for isolated context`() {
+ val c = IsolatedContext()
+
+ // Same component is returned for multiple calls, irrespective of the wrappers
+ assertNotNull(LauncherComponentProvider.get(c))
+ assertSame(
+ LauncherComponentProvider.get(c),
+ LauncherComponentProvider.get(ContextThemeWrapper(c, R.style.LauncherTheme)),
+ )
+
+ // Different than main application
+ assertNotSame(LauncherComponentProvider.get(c), LauncherComponentProvider.get(app))
+ }
+
+ @Test
+ fun `different components for different isolated context`() {
+ val c1 = IsolatedContext()
+ val c2 = IsolatedContext()
+
+ assertNotNull(LauncherComponentProvider.get(c1))
+ assertNotNull(LauncherComponentProvider.get(c2))
+ assertNotSame(LauncherComponentProvider.get(c1), LauncherComponentProvider.get(c2))
+ }
+
+ inner class IsolatedContext : ContextWrapper(app.createPackageContext(TEST_PACKAGE, 0)) {
+
+ override fun getApplicationContext(): Context = this
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
index e8f778f..7cd5da4 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.LauncherAppState
-import com.android.launcher3.icons.BitmapInfo
import com.android.launcher3.icons.waitForUpdateHandlerToFinish
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.util.Executors
@@ -161,9 +160,6 @@
assertWithMessage("Index $index was not highRes")
.that(items[index].bitmap.isNullOrLowRes)
.isFalse()
- assertWithMessage("Index $index was the default icon")
- .that(isDefaultIcon(items[index].bitmap))
- .isFalse()
}
}
@@ -172,17 +168,9 @@
assertWithMessage("Index $index was not lowRes")
.that(items[index].bitmap.isNullOrLowRes)
.isTrue()
- assertWithMessage("Index $index was the default icon")
- .that(isDefaultIcon(items[index].bitmap))
- .isFalse()
}
}
- private fun isDefaultIcon(bitmap: BitmapInfo) =
- LauncherAppState.getInstance(modelHelper.sandboxContext)
- .iconCache
- .isDefaultIcon(bitmap, modelHelper.sandboxContext.user)
-
/** Recreate DeviceProfiles after changing InvariantDeviceProfile */
private fun recreateSupportedDeviceProfiles() {
LauncherAppState.getIDP(modelHelper.sandboxContext).supportedProfiles =
diff --git a/tests/multivalentTests/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPoolTest.kt b/tests/multivalentTests/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPoolTest.kt
new file mode 100644
index 0000000..3afb0b5
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPoolTest.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.recyclerview
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.LayoutManager
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.util.Executors
+import com.android.launcher3.views.ActivityContext
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AllAppsRecyclerViewPoolTest<T> where T : Context, T : ActivityContext {
+
+ private lateinit var underTest: AllAppsRecyclerViewPool<T>
+ private lateinit var adapter: RecyclerView.Adapter<*>
+
+ @Mock private lateinit var parent: RecyclerView
+ @Mock private lateinit var itemView: View
+ @Mock private lateinit var layoutManager: LayoutManager
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ underTest = spy(AllAppsRecyclerViewPool())
+ adapter =
+ object : RecyclerView.Adapter<ViewHolder>() {
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
+ object : ViewHolder(itemView) {}
+
+ override fun getItemCount() = 0
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {}
+ }
+ underTest.setMaxRecycledViews(VIEW_TYPE, 20)
+ `when`(parent.layoutManager).thenReturn(layoutManager)
+ }
+
+ @Test
+ fun preinflate_success() {
+ underTest.preInflateAllAppsViewHolders(adapter, VIEW_TYPE, parent, 10) { 10 }
+
+ awaitTasksCompleted()
+ assertThat(underTest.getRecycledViewCount(VIEW_TYPE)).isEqualTo(10)
+ }
+
+ @Test
+ fun preinflate_not_triggered() {
+ underTest.preInflateAllAppsViewHolders(adapter, VIEW_TYPE, parent, 0) { 0 }
+
+ awaitTasksCompleted()
+ assertThat(underTest.getRecycledViewCount(VIEW_TYPE)).isEqualTo(0)
+ }
+
+ @Test
+ fun preinflate_cancel_before_runOnMainThread() {
+ underTest.preInflateAllAppsViewHolders(adapter, VIEW_TYPE, parent, 10) { 10 }
+ assertThat(underTest.mCancellableTask!!.canceled).isFalse()
+
+ underTest.clear()
+
+ awaitTasksCompleted()
+ verify(underTest, never()).putRecycledView(any(ViewHolder::class.java))
+ assertThat(underTest.mCancellableTask!!.canceled).isTrue()
+ assertThat(underTest.getRecycledViewCount(VIEW_TYPE)).isEqualTo(0)
+ }
+
+ @Test
+ fun preinflate_cancel_after_run() {
+ underTest.preInflateAllAppsViewHolders(adapter, VIEW_TYPE, parent, 10) { 10 }
+ assertThat(underTest.mCancellableTask!!.canceled).isFalse()
+ awaitTasksCompleted()
+
+ underTest.clear()
+
+ verify(underTest, times(10)).putRecycledView(any(ViewHolder::class.java))
+ assertThat(underTest.mCancellableTask!!.canceled).isTrue()
+ assertThat(underTest.getRecycledViewCount(VIEW_TYPE)).isEqualTo(0)
+ }
+
+ private fun awaitTasksCompleted() {
+ Executors.VIEW_PREINFLATION_EXECUTOR.submit<Any> { null }.get()
+ Executors.MAIN_EXECUTOR.submit<Any> { null }.get()
+ }
+
+ companion object {
+ private const val VIEW_TYPE: Int = 4
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java b/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
index ce682f1..eb25acf 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
@@ -29,7 +29,10 @@
import android.app.blob.BlobHandle;
import android.app.blob.BlobStoreManager;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Point;
import android.os.AsyncTask;
@@ -231,6 +234,15 @@
.adoptShellPermissionIdentity(Manifest.permission.WRITE_SECURE_SETTINGS);
}
+ /**
+ * Returns the activity info corresponding to the system app for the provided category
+ */
+ public static ActivityInfo resolveSystemAppInfo(String category) {
+ return getInstrumentation().getTargetContext().getPackageManager().resolveActivity(
+ new Intent(Intent.ACTION_MAIN).addCategory(category),
+ PackageManager.MATCH_SYSTEM_ONLY).activityInfo;
+ }
+
/** Interface to indicate a runnable which can throw any exception. */
public interface UncheckedRunnable {
/** Method to run the task */
diff --git a/tests/res/drawable/test_icon.xml b/tests/res/drawable/test_icon.xml
deleted file mode 100644
index 72ebfeb..0000000
--- a/tests/res/drawable/test_icon.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
- <background android:drawable="@android:color/white"/>
- <foreground>
- <color android:color="#FFFF0000" />
- </foreground>
- <monochrome>
- <vector android:width="48dp" android:height="48dp" android:viewportWidth="48.0" android:viewportHeight="48.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M0,24L48,24 48,48, 0,48 Z"/>
- </vector>
- </monochrome>
-</adaptive-icon>
diff --git a/tests/src/com/android/launcher3/LauncherIntentTest.java b/tests/src/com/android/launcher3/LauncherIntentTest.java
index aeeb42a..a3d9614 100644
--- a/tests/src/com/android/launcher3/LauncherIntentTest.java
+++ b/tests/src/com/android/launcher3/LauncherIntentTest.java
@@ -23,21 +23,27 @@
import android.platform.test.annotations.LargeTest;
import android.view.KeyEvent;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.allapps.SearchRecyclerView;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.BaseLauncherActivityTest;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@LargeTest
@RunWith(AndroidJUnit4.class)
-public class LauncherIntentTest extends AbstractLauncherUiTest<Launcher> {
+public class LauncherIntentTest extends BaseLauncherActivityTest<Launcher> {
public final Intent allAppsIntent = new Intent(Intent.ACTION_ALL_APPS);
+ @Before
+ public void setUp() {
+ loadLauncherSync();
+ }
+
@Test
public void testAllAppsIntent() {
// Try executing ALL_APPS intent
@@ -45,7 +51,6 @@
// A-Z view with Main adapter should be loaded
assertOnMainAdapterAToZView();
-
// Try Moving to search view now
moveToSearchView();
// Try executing ALL_APPS intent
@@ -63,12 +68,14 @@
// Search view should be in focus
waitForLauncherCondition("Search view is not in focus.",
launcher -> launcher.getAppsView().getSearchView().hasFocus());
- mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_C, 0);
+
+ injectKeyEvent(KeyEvent.KEYCODE_C, true);
// Upon key press, search recycler view should be loaded
waitForLauncherCondition("Search view not active.",
launcher -> launcher.getAppsView().getActiveRecyclerView()
instanceof SearchRecyclerView);
- mLauncher.unpressKeyCode(KeyEvent.KEYCODE_C, 0);
+
+ injectKeyEvent(KeyEvent.KEYCODE_C, false);
}
// Checks if main adapter view is selected, search bar is out of focus and scroller is at start.
diff --git a/tests/src/com/android/launcher3/allapps/KeyboardFocusTest.java b/tests/src/com/android/launcher3/allapps/KeyboardFocusTest.java
new file mode 100644
index 0000000..1e21ee5
--- /dev/null
+++ b/tests/src/com/android/launcher3/allapps/KeyboardFocusTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps;
+
+import android.view.KeyEvent;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.util.BaseLauncherActivityTest;
+import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
+import com.android.launcher3.views.ActivityContext;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KeyboardFocusTest extends BaseLauncherActivityTest<Launcher> {
+
+ @Test
+ public void testAllAppsFocusApp() {
+ loadLauncherSync();
+ goToState(LauncherState.ALL_APPS);
+ freezeAllApps();
+
+ injectKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, true);
+ injectKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, false);
+ waitForLauncherCondition("No focused child", launcher ->
+ launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild()
+ != null);
+ }
+
+ @Test
+ public void testAllAppsExitSearchAndFocusApp() {
+ loadLauncherSync();
+ goToState(LauncherState.ALL_APPS);
+ freezeAllApps();
+
+ executeOnLauncher(launcher -> launcher.getAppsView().getSearchView().requestFocus());
+ waitForLauncherCondition("Search view does not have focus.",
+ launcher -> launcher.getAppsView().getSearchView().hasFocus());
+
+ injectKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, true);
+ injectKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, false);
+ waitForLauncherCondition("No focused child", launcher ->
+ launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild()
+ != null);
+ }
+
+ @Test
+ @ScreenRecord //b/378167329
+ public void testAllAppsExitSearchAndFocusSearchResults() {
+ loadLauncherSync();
+ goToState(LauncherState.ALL_APPS);
+ freezeAllApps();
+
+ executeOnLauncher(launcher -> launcher.getAppsView().getSearchView().requestFocus());
+ waitForLauncherCondition("Search view does not have focus.",
+ launcher -> launcher.getAppsView().getSearchView().hasFocus());
+
+ injectKeyEvent(KeyEvent.KEYCODE_C, true);
+ waitForLauncherCondition("Search view not active.",
+ launcher -> launcher.getAppsView().getActiveRecyclerView()
+ instanceof SearchRecyclerView);
+ injectKeyEvent(KeyEvent.KEYCODE_C, false);
+
+ executeOnLauncher(launcher -> launcher.getAppsView().getSearchUiManager().getEditText()
+ .hideKeyboard(/* clearFocus= */ false));
+ waitForLauncherCondition("Keyboard still visible.",
+ ActivityContext::isSoftwareKeyboardHidden);
+
+ injectKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, true);
+ injectKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, false);
+ waitForLauncherCondition("No focused child", launcher ->
+ launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild()
+ != null);
+ }
+}
diff --git a/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java b/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java
deleted file mode 100644
index 4e627a9..0000000
--- a/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.allapps;
-
-import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.view.KeyEvent;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.tapl.HomeAllApps;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
-import com.android.launcher3.util.rule.TestStabilityRule;
-import com.android.launcher3.views.ActivityContext;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TaplKeyboardFocusTest extends AbstractLauncherUiTest<Launcher> {
-
- @Test
- public void testAllAppsFocusApp() {
- final HomeAllApps allApps = mLauncher.goHome().switchToAllApps();
- assertTrue("Launcher internal state is not All Apps",
- isInState(() -> LauncherState.ALL_APPS));
- allApps.freeze();
- try {
- mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_DPAD_DOWN, 0);
- executeOnLauncher(launcher -> assertNotNull("No focused child.",
- launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild()));
- } finally {
- allApps.unfreeze();
- }
- }
-
- @Test
- public void testAllAppsExitSearchAndFocusApp() {
- final HomeAllApps allApps = mLauncher.goHome().switchToAllApps();
- assertTrue("Launcher internal state is not All Apps",
- isInState(() -> LauncherState.ALL_APPS));
- allApps.freeze();
- try {
- executeOnLauncher(launcher -> launcher.getAppsView().getSearchView().requestFocus());
- waitForLauncherCondition("Search view does not have focus.",
- launcher -> launcher.getAppsView().getSearchView().hasFocus());
-
- mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_DPAD_DOWN, 0);
- executeOnLauncher(launcher -> assertNotNull("No focused child.",
- launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild()));
- } finally {
- allApps.unfreeze();
- }
- }
-
- @Test
- public void testAllAppsExitSearchAndFocusSearchResults() {
- final HomeAllApps allApps = mLauncher.goHome().switchToAllApps();
- assertTrue("Launcher internal state is not All Apps",
- isInState(() -> LauncherState.ALL_APPS));
- allApps.freeze();
- try {
- executeOnLauncher(launcher -> launcher.getAppsView().getSearchView().requestFocus());
- waitForLauncherCondition("Search view does not have focus.",
- launcher -> launcher.getAppsView().getSearchView().hasFocus());
-
- mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_C, 0);
- waitForLauncherCondition("Search view not active.",
- launcher -> launcher.getAppsView().getActiveRecyclerView()
- instanceof SearchRecyclerView);
- mLauncher.unpressKeyCode(KeyEvent.KEYCODE_C, 0);
-
- executeOnLauncher(launcher -> launcher.getAppsView().getSearchUiManager().getEditText()
- .hideKeyboard(/* clearFocus= */ false));
- waitForLauncherCondition("Keyboard still visible.",
- ActivityContext::isSoftwareKeyboardHidden);
-
- mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_DPAD_DOWN, 0);
- mLauncher.unpressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN, 0);
- waitForLauncherCondition("No focused child", launcher ->
- launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild()
- != null);
- } finally {
- allApps.unfreeze();
- }
- }
-}
diff --git a/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
similarity index 73%
rename from tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java
rename to tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
index 1500538..34b292c 100644
--- a/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java
+++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
@@ -1,18 +1,19 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * 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
+ * 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.
+ * 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.compat;
import static com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING;
@@ -27,32 +28,31 @@
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.text.TextUtils;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.tapl.AllApps;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.BaseLauncherActivityTest;
import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
import com.android.launcher3.util.TestUtil;
-import com.android.launcher3.util.rule.ViewCaptureRule;
import org.junit.After;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
+import java.util.Arrays;
import java.util.UUID;
-
/**
* Test to verify promise icon flow.
*/
@LargeTest
@RunWith(AndroidJUnit4.class)
-public class TaplPromiseIconUiTest extends AbstractLauncherUiTest<Launcher> {
+public class PromiseIconUiTest extends BaseLauncherActivityTest<Launcher> {
@Rule
public final CheckFlagsRule mCheckFlagsRule =
@@ -64,19 +64,17 @@
private int mSessionId = -1;
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
- mDevice.pressHome();
- waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
- waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
+ loadLauncherSync();
+ goToState(LauncherState.NORMAL);
mSessionId = -1;
}
@After
public void tearDown() throws IOException {
if (mSessionId > -1) {
- mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+ targetContext().getPackageManager().getPackageInstaller().abandonSession(mSessionId);
}
TestUtil.uninstallDummyApp();
}
@@ -90,7 +88,7 @@
params.setAppLabel(label);
params.setAppIcon(icon);
params.setInstallReason(PackageManager.INSTALL_REASON_USER);
- return mTargetContext.getPackageManager().getPackageInstaller().createSession(params);
+ return targetContext().getPackageManager().getPackageInstaller().createSession(params);
}
@Test
@@ -108,7 +106,7 @@
launcher.getWorkspace().getFirstMatch(findPromiseApp) != null);
// Remove session
- mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+ targetContext().getPackageManager().getPackageInstaller().abandonSession(mSessionId);
mSessionId = -1;
// Verify promise icon is removed
@@ -117,7 +115,6 @@
}
@Test
- @ViewCaptureRule.MayProduceNoFrames
public void testPromiseIcon_notAddedFromIneligibleSession() throws Throwable {
final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
final ItemOperator findPromiseApp = (info, view) ->
@@ -138,7 +135,8 @@
@RequiresFlagsEnabled(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
public void testPromiseIcon_addedArchivedApp() throws Throwable {
installDummyAppAndWaitForUIUpdate();
- assertThat(mDevice.executeShellCommand(String.format("pm archive %s", DUMMY_PACKAGE)))
+ assertThat(executeShellCommand(
+ String.format("pm archive %s", DUMMY_PACKAGE)))
.isEqualTo("Success\n");
// Create and add test session
@@ -148,28 +146,19 @@
// Verify promise icon is added to all apps view. The icon may not be added to the
// workspace even if there might be no icon present for archived app. But icon will
// always be in all apps view. In case an icon is not added, an exception would be thrown.
- final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+ goToState(LauncherState.ALL_APPS);
// Wait for the promise icon to be added.
waitForLauncherCondition(
DUMMY_PACKAGE + " app was not found on all apps after being archived",
- launcher -> {
- try {
- allApps.getAppIcon(DUMMY_LABEL);
- } catch (Throwable t) {
- return false;
- }
- return true;
- });
-
- // Remove session
- mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
- mSessionId = -1;
+ launcher -> Arrays.stream(launcher.getAppsView().getAppsStore().getApps())
+ .filter(info -> DUMMY_LABEL.equals(info.title.toString()))
+ .findAny()
+ .isPresent());
}
private void installDummyAppAndWaitForUIUpdate() throws IOException {
TestUtil.installDummyApp();
- mLauncher.waitForModelQueueCleared();
- mLauncher.waitForLauncherInitialized();
+ loadLauncherSync();
}
}
diff --git a/tests/src/com/android/launcher3/dragging/TaplDragTest.java b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
index 59e1f99..e2f9feb9a 100644
--- a/tests/src/com/android/launcher3/dragging/TaplDragTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
@@ -64,7 +64,6 @@
@Test
@PortraitLandscape
@PlatinumTest(focusArea = "launcher")
- @ScreenRecordRule.ScreenRecord // b/353600888
public void testDragToFolder() {
// TODO: add the use case to drag an icon to an existing folder. Currently it either fails
// on tablets or phones due to difference in resolution.
@@ -97,7 +96,6 @@
* icon left.
*/
@Test
- @ScreenRecordRule.ScreenRecord // b/353600888
public void testDragOutOfFolder() {
final HomeAppIcon playStoreIcon = createShortcutIfNotExist(STORE_APP_NAME, 0, 1);
final HomeAppIcon photosIcon = createShortcutInCenterIfNotExist(PHOTOS_APP_NAME);
diff --git a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
index 44b8ff8..1816030 100644
--- a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
@@ -69,8 +69,7 @@
private void verifyAppUninstalledFromAllApps(Workspace workspace, String appName) {
final HomeAllApps allApps = workspace.switchToAllApps();
Wait.atMost(appName + " app was found on all apps after being uninstalled",
- () -> allApps.tryGetAppIcon(appName) == null,
- DEFAULT_UI_TIMEOUT, mLauncher);
+ () -> allApps.tryGetAppIcon(appName) == null, mLauncher);
}
private void installDummyAppAndWaitForUIUpdate() throws IOException {
diff --git a/tests/src/com/android/launcher3/model/GridMigrationTest.kt b/tests/src/com/android/launcher3/model/GridMigrationTest.kt
index 666ec16..379e98d 100644
--- a/tests/src/com/android/launcher3/model/GridMigrationTest.kt
+++ b/tests/src/com/android/launcher3/model/GridMigrationTest.kt
@@ -101,6 +101,7 @@
dst.gridState,
dst.dbHelper,
src.dbHelper.readableDatabase,
+ false,
)
} else {
GridSizeMigrationDBController.migrateGridIfNeeded(
@@ -109,6 +110,7 @@
dst.gridState,
dst.dbHelper,
src.dbHelper.readableDatabase,
+ false,
)
}
}
diff --git a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
index 60385a7..2e2b6cd 100644
--- a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
+++ b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
@@ -20,6 +20,7 @@
import com.android.launcher3.DeviceProfile
import com.android.launcher3.Flags
import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.util.rule.setFlags
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,11 +36,11 @@
@Before
fun setUp() {
- if (instance.decoupleDepth) {
- setFlagsRule.enableFlags(Flags.FLAG_ENABLE_SCALING_REVEAL_HOME_ANIMATION)
- } else {
- setFlagsRule.disableFlags(Flags.FLAG_ENABLE_SCALING_REVEAL_HOME_ANIMATION)
- }
+ setFlagsRule.setFlags(
+ instance.decoupleDepth,
+ Flags.FLAG_ENABLE_SCALING_REVEAL_HOME_ANIMATION,
+ )
+ setFlagsRule.setFlags(false, Flags.FLAG_ONE_GRID_SPECS)
}
@Test
@@ -105,13 +106,13 @@
initializeVarsForTablet(
deviceSpec = deviceSpec,
isLandscape = isLandscape,
- isGestureMode = isGestureMode
+ isGestureMode = isGestureMode,
)
else ->
initializeVarsForPhone(
deviceSpec = deviceSpec,
isVerticalBar = isLandscape,
- isGestureMode = isGestureMode
+ isGestureMode = isGestureMode,
)
}
}
@@ -136,7 +137,7 @@
"twopanel-tablet",
gridName = "4_by_4",
isTaskbarPresentInApps = true,
- decoupleDepth = true
+ decoupleDepth = true,
),
)
}
@@ -145,7 +146,7 @@
val deviceName: String,
val gridName: String,
val isTaskbarPresentInApps: Boolean = false,
- val decoupleDepth: Boolean = false
+ val decoupleDepth: Boolean = false,
) {
fun filename(testName: String = ""): String {
val device =
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 206647a..8e4db5c 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -15,77 +15,41 @@
*/
package com.android.launcher3.ui;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
-
import static androidx.test.InstrumentationRegistry.getInstrumentation;
-import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.graphics.Point;
-import android.os.Debug;
import android.os.Process;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.platform.test.flag.junit.SetFlagsRule;
-import android.platform.test.rule.LimitDevicesRule;
import android.system.OsConstants;
import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
-import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.Until;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
-import com.android.launcher3.celllayout.FavoriteItemsTransaction;
-import com.android.launcher3.tapl.HomeAllApps;
-import com.android.launcher3.tapl.HomeAppIcon;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.ExtendedLongPressTimeoutRule;
import com.android.launcher3.util.rule.FailureWatcher;
-import com.android.launcher3.util.rule.SamplerRule;
-import com.android.launcher3.util.rule.ScreenRecordRule;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.util.rule.TestIsolationRule;
-import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.launcher3.util.rule.ViewCaptureRule;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
-import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
@@ -95,171 +59,51 @@
/**
* Base class for all instrumentation tests providing various utility methods.
*/
-public abstract class AbstractLauncherUiTest<LAUNCHER_TYPE extends Launcher> {
+public abstract class AbstractLauncherUiTest<LAUNCHER_TYPE extends Launcher>
+ extends BaseLauncherTaplTest {
- public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
- public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 10;
-
- public static final long DEFAULT_UI_TIMEOUT = TestUtil.DEFAULT_UI_TIMEOUT;
private static final String TAG = "AbstractLauncherUiTest";
- private static final long BYTES_PER_MEGABYTE = 1 << 20;
-
- private static boolean sDumpWasGenerated = false;
- private static boolean sActivityLeakReported = false;
- private static boolean sSeenKeyguard = false;
- private static boolean sFirstTimeWaitingForWizard = true;
-
- private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
-
protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR;
- protected final UiDevice mDevice = getUiDevice();
- protected final LauncherInstrumentation mLauncher = createLauncherInstrumentation();
-
- @NonNull
- public static LauncherInstrumentation createLauncherInstrumentation() {
- waitForSetupWizardDismissal(); // precondition for creating LauncherInstrumentation
- return new LauncherInstrumentation(true);
- }
-
- protected Context mTargetContext;
- protected String mTargetPackage;
- private int mLauncherPid;
-
- private final ActivityManager.MemoryInfo mMemoryInfo = new ActivityManager.MemoryInfo();
- private final ActivityManager mActivityManager;
- private long mMemoryBefore;
-
- /** Detects activity leaks and throws an exception if a leak is found. */
- public static void checkDetectedLeaks(LauncherInstrumentation launcher) {
- checkDetectedLeaks(launcher, false);
- }
-
- /** 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
-
- if (sActivityLeakReported) return;
-
- // Check whether activity leak detector has found leaked activities.
- Wait.atMost(() -> getActivityLeakErrorMessage(launcher, requireOneActiveActivity),
- () -> {
- launcher.forceGc();
- return MAIN_EXECUTOR.submit(
- () -> launcher.noLeakedActivities(requireOneActiveActivity)).get();
- }, DEFAULT_UI_TIMEOUT, launcher);
- }
-
- public static String getAppPackageName() {
- return getInstrumentation().getContext().getPackageName();
- }
-
- private static String getActivityLeakErrorMessage(LauncherInstrumentation launcher,
- boolean requireOneActiveActivity) {
- sActivityLeakReported = true;
- return "Activity leak detector has found leaked activities, requirining 1 activity: "
- + requireOneActiveActivity + "; "
- + dumpHprofData(launcher, false, requireOneActiveActivity) + ".";
- }
-
- private static String dumpHprofData(LauncherInstrumentation launcher, boolean intentionalLeak,
- boolean requireOneActiveActivity) {
- if (intentionalLeak) return "intentional leak; not generating dump";
-
- String result;
- if (sDumpWasGenerated) {
- result = "dump has already been generated by another test";
- } else {
- try {
- final String fileName =
- getInstrumentation().getTargetContext().getFilesDir().getPath()
- + "/ActivityLeakHeapDump.hprof";
- if (TestHelpers.isInLauncherProcess()) {
- Debug.dumpHprofData(fileName);
- } else {
- final UiDevice device = getUiDevice();
- device.executeShellCommand(
- "am dumpheap " + device.getLauncherPackageName() + " " + fileName);
- }
- Log.d(TAG, "Saved leak dump, the leak is still present: "
- + !launcher.noLeakedActivities(requireOneActiveActivity));
- sDumpWasGenerated = true;
- result = "saved memory dump as an artifact";
- } catch (Throwable e) {
- Log.e(TAG, "dumpHprofData failed", e);
- result = "failed to save memory dump";
- }
- }
- return result + ". Full list of activities: " + launcher.getRootedActivitiesList();
- }
protected AbstractLauncherUiTest() {
- mActivityManager = InstrumentationRegistry.getContext()
- .getSystemService(ActivityManager.class);
- mLauncher.enableCheckEventsForSuccessfulGestures();
- mLauncher.setAnomalyChecker(AbstractLauncherUiTest::verifyKeyguardInvisible);
- try {
- mDevice.setOrientationNatural();
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
if (TestHelpers.isInLauncherProcess()) {
Utilities.enableRunningInTestHarnessForTests();
mLauncher.setSystemHealthSupplier(startTime -> TestCommandReceiver.callCommand(
TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString())
.getString("result"));
}
- mLauncher.enableDebugTracing();
- // Avoid double-reporting of Launcher crashes.
- mLauncher.setOnLauncherCrashed(() -> mLauncherPid = 0);
}
- @Rule
- public ShellCommandRule mDisableHeadsUpNotification =
- ShellCommandRule.disableHeadsUpNotification();
-
- @Rule
- public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule();
-
- @Rule
- public SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
-
- @Rule
- public ExtendedLongPressTimeoutRule mLongPressTimeoutRule = new ExtendedLongPressTimeoutRule();
-
- @Rule
- public LimitDevicesRule mlimitDevicesRule = new LimitDevicesRule();
-
+ /**
+ * @deprecated call {@link #performInitialization} instead
+ */
+ @Deprecated
public static void initialize(AbstractLauncherUiTest test) throws Exception {
- test.reinitializeLauncherData();
- test.mDevice.pressHome();
- test.waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
- test.waitForState("Launcher internal state didn't switch to Home",
- () -> LauncherState.NORMAL);
- test.waitForResumed("Launcher internal state is still Background");
+ test.performInitialization();
+ }
+
+ @Override
+ protected void performInitialization() {
+ reinitializeLauncherData();
+ mDevice.pressHome();
// Check that we switched to home.
- test.mLauncher.getWorkspace();
- AbstractLauncherUiTest.checkDetectedLeaks(test.mLauncher, true);
+ mLauncher.getWorkspace();
+
+ waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
+ waitForState("Launcher internal state didn't switch to Home",
+ () -> LauncherState.NORMAL);
+ waitForResumed("Launcher internal state is still Background");
+
+ checkDetectedLeaks(mLauncher, true);
}
- protected void clearPackageData(String pkg) throws IOException, InterruptedException {
- assertTrue("pm clear command failed",
- mDevice.executeShellCommand("pm clear " + pkg)
- .contains("Success"));
- assertTrue("pm wait-for-handler command failed",
- mDevice.executeShellCommand("pm wait-for-handler")
- .contains("Success"));
- }
-
+ @Override
protected TestRule getRulesInsideActivityMonitor() {
final ViewCaptureRule viewCaptureRule = new ViewCaptureRule(
Launcher.ACTIVITY_TRACKER::getCreatedContext);
final RuleChain inner = RuleChain
- .outerRule(new PortraitLandscapeRunner<LAUNCHER_TYPE>(this))
+ .outerRule(new PortraitLandscapeRunner<>(this))
.around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData))
// .around(viewCaptureRule) // b/315482167
.around(new TestIsolationRule(mLauncher, true));
@@ -269,181 +113,12 @@
: inner;
}
- @Rule
- public TestRule mOrderSensitiveRules = RuleChain
- .outerRule(new SamplerRule())
- .around(new TestStabilityRule())
- .around(getRulesInsideActivityMonitor());
-
- public UiDevice getDevice() {
- return mDevice;
- }
-
- @Before
- public void setUp() throws Exception {
- mLauncher.onTestStart();
-
- final String launcherPackageName = mDevice.getLauncherPackageName();
- try {
- final Context context = InstrumentationRegistry.getContext();
- final PackageManager pm = context.getPackageManager();
- final PackageInfo launcherPackage = pm.getPackageInfo(launcherPackageName, 0);
-
- if (!launcherPackage.versionName.equals("BuildFromAndroidStudio")) {
- Assert.assertEquals("Launcher version doesn't match tests version",
- pm.getPackageInfo(context.getPackageName(), 0).getLongVersionCode(),
- launcherPackage.getLongVersionCode());
- }
- } catch (PackageManager.NameNotFoundException e) {
- throw new RuntimeException(e);
- }
-
- mLauncherPid = 0;
-
- mTargetContext = InstrumentationRegistry.getTargetContext();
- mTargetPackage = mTargetContext.getPackageName();
- mLauncherPid = mLauncher.getPid();
-
- UserManager userManager = mTargetContext.getSystemService(UserManager.class);
- if (userManager != null) {
- for (UserHandle userHandle : userManager.getUserProfiles()) {
- if (!userHandle.isSystem()) {
- mDevice.executeShellCommand(
- "pm remove-user --wait " + userHandle.getIdentifier());
- }
- }
- }
-
- onTestStart();
-
- initialize(this);
- }
-
- private long getAvailableMemory() {
- mActivityManager.getMemoryInfo(mMemoryInfo);
-
- return Math.divideExact(mMemoryInfo.availMem, BYTES_PER_MEGABYTE);
- }
-
- @Before
- public void saveMemoryBefore() {
- mMemoryBefore = getAvailableMemory();
- }
-
- @After
- public void logMemoryAfter() {
- long memoryAfter = getAvailableMemory();
-
- Log.d(TAG, "Available memory: before=" + mMemoryBefore
- + "MB, after=" + memoryAfter
- + "MB, delta=" + (memoryAfter - mMemoryBefore) + "MB");
- }
-
- /** Method that should be called when a test starts. */
- public static void onTestStart() {
- waitForSetupWizardDismissal();
-
- if (TestStabilityRule.isPresubmit()) {
- aggressivelyUnlockSysUi();
- } else {
- verifyKeyguardInvisible();
- }
- }
-
- private static boolean hasSystemUiObject(String resId) {
- return getUiDevice().hasObject(
- By.res(SYSTEMUI_PACKAGE, resId));
- }
-
- @NonNull
- private static UiDevice getUiDevice() {
- return UiDevice.getInstance(getInstrumentation());
- }
-
- private static void aggressivelyUnlockSysUi() {
- final UiDevice device = getUiDevice();
- for (int i = 0; i < 10 && hasSystemUiObject("keyguard_status_view"); ++i) {
- Log.d(TAG, "Before attempting to unlock the phone");
- try {
- device.executeShellCommand("input keyevent 82");
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- device.waitForIdle();
- }
- Assert.assertTrue("Keyguard still visible",
- TestHelpers.wait(
- Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000));
- Log.d(TAG, "Keyguard is not visible");
- }
-
- /** Waits for setup wizard to go away. */
- private static void waitForSetupWizardDismissal() {
- if (sFirstTimeWaitingForWizard) {
- try {
- getUiDevice().executeShellCommand(
- "am force-stop com.google.android.setupwizard");
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- final boolean wizardDismissed = TestHelpers.wait(
- Until.gone(By.pkg("com.google.android.setupwizard").depth(0)),
- sFirstTimeWaitingForWizard ? 120000 : 0);
- sFirstTimeWaitingForWizard = false;
- Assert.assertTrue("Setup wizard is still visible", wizardDismissed);
- }
-
- /** Asserts that keyguard is not visible */
- public static void verifyKeyguardInvisible() {
- final boolean keyguardAlreadyVisible = sSeenKeyguard;
-
- sSeenKeyguard = sSeenKeyguard
- || !TestHelpers.wait(
- Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000);
-
- Assert.assertFalse(
- "Keyguard is visible, which is likely caused by a crash in SysUI, seeing keyguard"
- + " for the first time = "
- + !keyguardAlreadyVisible,
- sSeenKeyguard);
- }
-
- @After
- public void verifyLauncherState() {
- try {
- // Limits UI tests affecting tests running after them.
- mDevice.pressHome();
- mLauncher.waitForLauncherInitialized();
- if (mLauncherPid != 0) {
- assertEquals("Launcher crashed, pid mismatch:",
- mLauncherPid, mLauncher.getPid().intValue());
- }
- } finally {
- mLauncher.onTestFinish();
- }
- }
-
- protected void reinitializeLauncherData() {
- reinitializeLauncherData(false);
- }
-
- protected void reinitializeLauncherData(boolean clearWorkspace) {
- if (clearWorkspace) {
- mLauncher.clearLauncherData();
- } else {
- mLauncher.reinitializeLauncherData();
- }
- mLauncher.waitForLauncherInitialized();
- }
-
/**
* Runs the callback on the UI thread and returns the result.
*/
protected <T> T getOnUiThread(final Callable<T> callback) {
try {
- return mMainThreadExecutor.submit(callback).get(DEFAULT_UI_TIMEOUT,
+ return mMainThreadExecutor.submit(callback).get(TestUtil.DEFAULT_UI_TIMEOUT,
TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
Log.e(TAG, "Timeout in getOnUiThread, sending SIGABRT", e);
@@ -498,13 +173,7 @@
// flakiness.
protected void waitForLauncherCondition(String
message, Function<LAUNCHER_TYPE, Boolean> condition) {
- waitForLauncherCondition(message, condition, DEFAULT_ACTIVITY_TIMEOUT);
- }
-
- // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
- // flakiness.
- protected <O> O getOnceNotNull(String message, Function<LAUNCHER_TYPE, O> f) {
- return getOnceNotNull(message, f, DEFAULT_ACTIVITY_TIMEOUT);
+ waitForLauncherCondition(message, condition, TestUtil.DEFAULT_UI_TIMEOUT);
}
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
@@ -513,12 +182,12 @@
String message, Function<LAUNCHER_TYPE, Boolean> condition, long timeout) {
verifyKeyguardInvisible();
if (!TestHelpers.isInLauncherProcess()) return;
- Wait.atMost(message, () -> getFromLauncher(condition), timeout, mLauncher);
+ Wait.atMost(message, () -> getFromLauncher(condition), mLauncher, timeout);
}
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
// flakiness.
- protected <T> T getOnceNotNull(String message, Function<LAUNCHER_TYPE, T> f, long timeout) {
+ protected <T> T getOnceNotNull(String message, Function<LAUNCHER_TYPE, T> f) {
if (!TestHelpers.isInLauncherProcess()) return null;
final Object[] output = new Object[1];
@@ -526,7 +195,7 @@
final Object fromLauncher = getFromLauncher(f);
output[0] = fromLauncher;
return fromLauncher != null;
- }, timeout, mLauncher);
+ }, mLauncher);
return (T) output[0];
}
@@ -540,44 +209,7 @@
Wait.atMost(message, () -> {
testThreadAction.run();
return getFromLauncher(condition);
- }, timeout, mLauncher);
- }
-
- protected LauncherActivityInfo getSettingsApp() {
- return mTargetContext.getSystemService(LauncherApps.class)
- .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
- }
-
- /**
- * Broadcast receiver which blocks until the result is received.
- */
- public class BlockingBroadcastReceiver extends BroadcastReceiver {
-
- private final CountDownLatch latch = new CountDownLatch(1);
- private Intent mIntent;
-
- public BlockingBroadcastReceiver(String action) {
- mTargetContext.registerReceiver(this, new IntentFilter(action),
- Context.RECEIVER_EXPORTED/*UNAUDITED*/);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- mIntent = intent;
- latch.countDown();
- }
-
- public Intent blockingGetIntent() throws InterruptedException {
- assertTrue("Timed Out", latch.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS));
- mTargetContext.unregisterReceiver(this);
- return mIntent;
- }
-
- public Intent blockingGetExtraIntent() throws InterruptedException {
- Intent intent = blockingGetIntent();
- return intent == null ? null : (Intent) intent.getParcelableExtra(
- Intent.EXTRA_INTENT);
- }
+ }, mLauncher, timeout);
}
public static void startAppFast(String packageName) {
@@ -633,20 +265,13 @@
}
getInstrumentation().getTargetContext().startActivity(intent);
assertTrue("App didn't start: " + selector,
- TestHelpers.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
+ TestHelpers.wait(Until.hasObject(selector), TestUtil.DEFAULT_UI_TIMEOUT));
// Wait for the Launcher to stop.
final LauncherInstrumentation launcherInstrumentation = new LauncherInstrumentation();
Wait.atMost("Launcher activity didn't stop",
() -> !launcherInstrumentation.isLauncherActivityStarted(),
- DEFAULT_ACTIVITY_TIMEOUT, launcherInstrumentation);
- }
-
- public static ActivityInfo resolveSystemAppInfo(String category) {
- return getInstrumentation().getContext().getPackageManager().resolveActivity(
- new Intent(Intent.ACTION_MAIN).addCategory(category),
- PackageManager.MATCH_SYSTEM_ONLY).
- activityInfo;
+ launcherInstrumentation);
}
@@ -662,8 +287,7 @@
launcher.finish();
}
});
- waitForLauncherCondition(
- "Launcher still active", launcher -> launcher == null, DEFAULT_UI_TIMEOUT);
+ waitForLauncherCondition("Launcher still active", launcher -> launcher == null);
}
protected boolean isInLaunchedApp(LAUNCHER_TYPE launcher) {
@@ -682,45 +306,4 @@
protected void onLauncherActivityClose(LAUNCHER_TYPE launcher) {
}
-
- protected HomeAppIcon createShortcutInCenterIfNotExist(String name) {
- Point dimension = mLauncher.getWorkspace().getIconGridDimensions();
- return createShortcutIfNotExist(name, dimension.x / 2, dimension.y / 2);
- }
-
- protected HomeAppIcon createShortcutIfNotExist(String name, Point cellPosition) {
- return createShortcutIfNotExist(name, cellPosition.x, cellPosition.y);
- }
-
- protected HomeAppIcon createShortcutIfNotExist(String name, int cellX, int cellY) {
- HomeAppIcon homeAppIcon = mLauncher.getWorkspace().tryGetWorkspaceAppIcon(name);
- Log.d(ICON_MISSING, "homeAppIcon: " + homeAppIcon + " name: " + name +
- " cell: " + cellX + ", " + cellY);
- if (homeAppIcon == null) {
- HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
- allApps.freeze();
- try {
- allApps.getAppIcon(name).dragToWorkspace(cellX, cellY);
- } finally {
- allApps.unfreeze();
- }
- homeAppIcon = mLauncher.getWorkspace().getWorkspaceAppIcon(name);
- }
- return homeAppIcon;
- }
-
- protected void commitTransactionAndLoadHome(FavoriteItemsTransaction transaction) {
- transaction.commit();
-
- // Launch the home activity
- UiDevice.getInstance(getInstrumentation()).pressHome();
- mLauncher.waitForLauncherInitialized();
- }
-
- /** Clears all recent tasks */
- protected void clearAllRecentTasks() {
- if (!mLauncher.getRecentTasks().isEmpty()) {
- mLauncher.goHome().switchToOverview().dismissAllTasks();
- }
- }
}
diff --git a/tests/src/com/android/launcher3/ui/BaseLauncherTaplTest.java b/tests/src/com/android/launcher3/ui/BaseLauncherTaplTest.java
new file mode 100644
index 0000000..8449853
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/BaseLauncherTaplTest.java
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.ui;
+
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Point;
+import android.os.Debug;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.platform.test.rule.LimitDevicesRule;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import com.android.launcher3.celllayout.FavoriteItemsTransaction;
+import com.android.launcher3.tapl.HomeAllApps;
+import com.android.launcher3.tapl.HomeAppIcon;
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.util.TestUtil;
+import com.android.launcher3.util.Wait;
+import com.android.launcher3.util.rule.ExtendedLongPressTimeoutRule;
+import com.android.launcher3.util.rule.FailureWatcher;
+import com.android.launcher3.util.rule.SamplerRule;
+import com.android.launcher3.util.rule.ScreenRecordRule;
+import com.android.launcher3.util.rule.ShellCommandRule;
+import com.android.launcher3.util.rule.TestIsolationRule;
+import com.android.launcher3.util.rule.TestStabilityRule;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Base class for all TAPL tests in Launcher providing various utility methods.
+ */
+public abstract class BaseLauncherTaplTest {
+
+ public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
+ public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 10;
+
+ public static final long DEFAULT_UI_TIMEOUT = TestUtil.DEFAULT_UI_TIMEOUT;
+ private static final String TAG = "BaseLauncherTaplTest";
+
+ private static final long BYTES_PER_MEGABYTE = 1 << 20;
+
+ private static boolean sDumpWasGenerated = false;
+ private static boolean sActivityLeakReported = false;
+ private static boolean sSeenKeyguard = false;
+ private static boolean sFirstTimeWaitingForWizard = true;
+
+ private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+
+ protected final UiDevice mDevice = getUiDevice();
+ protected final LauncherInstrumentation mLauncher = createLauncherInstrumentation();
+
+ @NonNull
+ public static LauncherInstrumentation createLauncherInstrumentation() {
+ waitForSetupWizardDismissal(); // precondition for creating LauncherInstrumentation
+ return new LauncherInstrumentation(true);
+ }
+
+ protected Context mTargetContext;
+ protected String mTargetPackage;
+ private int mLauncherPid;
+
+ private final ActivityManager.MemoryInfo mMemoryInfo = new ActivityManager.MemoryInfo();
+ private final ActivityManager mActivityManager;
+ private long mMemoryBefore;
+
+ /** Detects activity leaks and throws an exception if a leak is found. */
+ public static void checkDetectedLeaks(LauncherInstrumentation launcher) {
+ checkDetectedLeaks(launcher, false);
+ }
+
+ /** 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
+
+ if (sActivityLeakReported) return;
+
+ // Check whether activity leak detector has found leaked activities.
+ Wait.atMost(() -> getActivityLeakErrorMessage(launcher, requireOneActiveActivity),
+ () -> {
+ launcher.forceGc();
+ return MAIN_EXECUTOR.submit(
+ () -> launcher.noLeakedActivities(requireOneActiveActivity)).get();
+ }, launcher, DEFAULT_UI_TIMEOUT);
+ }
+
+ public static String getAppPackageName() {
+ return getInstrumentation().getContext().getPackageName();
+ }
+
+ private static String getActivityLeakErrorMessage(LauncherInstrumentation launcher,
+ boolean requireOneActiveActivity) {
+ sActivityLeakReported = true;
+ return "Activity leak detector has found leaked activities, requirining 1 activity: "
+ + requireOneActiveActivity + "; "
+ + dumpHprofData(launcher, false, requireOneActiveActivity) + ".";
+ }
+
+ private static String dumpHprofData(LauncherInstrumentation launcher, boolean intentionalLeak,
+ boolean requireOneActiveActivity) {
+ if (intentionalLeak) return "intentional leak; not generating dump";
+
+ String result;
+ if (sDumpWasGenerated) {
+ result = "dump has already been generated by another test";
+ } else {
+ try {
+ final String fileName =
+ getInstrumentation().getTargetContext().getFilesDir().getPath()
+ + "/ActivityLeakHeapDump.hprof";
+ if (TestHelpers.isInLauncherProcess()) {
+ Debug.dumpHprofData(fileName);
+ } else {
+ final UiDevice device = getUiDevice();
+ device.executeShellCommand(
+ "am dumpheap " + device.getLauncherPackageName() + " " + fileName);
+ }
+ Log.d(TAG, "Saved leak dump, the leak is still present: "
+ + !launcher.noLeakedActivities(requireOneActiveActivity));
+ sDumpWasGenerated = true;
+ result = "saved memory dump as an artifact";
+ } catch (Throwable e) {
+ Log.e(TAG, "dumpHprofData failed", e);
+ result = "failed to save memory dump";
+ }
+ }
+ return result + ". Full list of activities: " + launcher.getRootedActivitiesList();
+ }
+
+ protected BaseLauncherTaplTest() {
+ mActivityManager = InstrumentationRegistry.getContext()
+ .getSystemService(ActivityManager.class);
+ mLauncher.enableCheckEventsForSuccessfulGestures();
+ mLauncher.setAnomalyChecker(BaseLauncherTaplTest::verifyKeyguardInvisible);
+ try {
+ mDevice.setOrientationNatural();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ mLauncher.enableDebugTracing();
+ // Avoid double-reporting of Launcher crashes.
+ mLauncher.setOnLauncherCrashed(() -> mLauncherPid = 0);
+ }
+
+ @Rule
+ public ShellCommandRule mDisableHeadsUpNotification =
+ ShellCommandRule.disableHeadsUpNotification();
+
+ @Rule
+ public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule();
+
+ @Rule
+ public SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
+ @Rule
+ public ExtendedLongPressTimeoutRule mLongPressTimeoutRule = new ExtendedLongPressTimeoutRule();
+
+ @Rule
+ public LimitDevicesRule mlimitDevicesRule = new LimitDevicesRule();
+
+ protected void performInitialization() {
+ reinitializeLauncherData();
+ mDevice.pressHome();
+ // Check that we switched to home.
+ mLauncher.getWorkspace();
+ checkDetectedLeaks(mLauncher, true);
+ }
+
+ protected void clearPackageData(String pkg) throws IOException, InterruptedException {
+ assertTrue("pm clear command failed",
+ mDevice.executeShellCommand("pm clear " + pkg)
+ .contains("Success"));
+ assertTrue("pm wait-for-handler command failed",
+ mDevice.executeShellCommand("pm wait-for-handler")
+ .contains("Success"));
+ }
+
+ protected TestRule getRulesInsideActivityMonitor() {
+ final RuleChain inner = RuleChain
+ .outerRule(new FailureWatcher(mLauncher, null))
+ .around(new TestIsolationRule(mLauncher, true));
+ return TestHelpers.isInLauncherProcess()
+ ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner)
+ : inner;
+ }
+
+ @Rule
+ public TestRule mOrderSensitiveRules = RuleChain
+ .outerRule(new SamplerRule())
+ .around(new TestStabilityRule())
+ .around(getRulesInsideActivityMonitor());
+
+ public UiDevice getDevice() {
+ return mDevice;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mLauncher.onTestStart();
+
+ final String launcherPackageName = mDevice.getLauncherPackageName();
+ try {
+ final Context context = InstrumentationRegistry.getContext();
+ final PackageManager pm = context.getPackageManager();
+ final PackageInfo launcherPackage = pm.getPackageInfo(launcherPackageName, 0);
+
+ if (!launcherPackage.versionName.equals("BuildFromAndroidStudio")) {
+ Assert.assertEquals("Launcher version doesn't match tests version",
+ pm.getPackageInfo(context.getPackageName(), 0).getLongVersionCode(),
+ launcherPackage.getLongVersionCode());
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ mLauncherPid = 0;
+
+ mTargetContext = InstrumentationRegistry.getTargetContext();
+ mTargetPackage = mTargetContext.getPackageName();
+ mLauncherPid = mLauncher.getPid();
+
+ UserManager userManager = mTargetContext.getSystemService(UserManager.class);
+ if (userManager != null) {
+ for (UserHandle userHandle : userManager.getUserProfiles()) {
+ if (!userHandle.isSystem()) {
+ mDevice.executeShellCommand(
+ "pm remove-user --wait " + userHandle.getIdentifier());
+ }
+ }
+ }
+
+ onTestStart();
+ performInitialization();
+ }
+
+ private long getAvailableMemory() {
+ mActivityManager.getMemoryInfo(mMemoryInfo);
+
+ return Math.divideExact(mMemoryInfo.availMem, BYTES_PER_MEGABYTE);
+ }
+
+ @Before
+ public void saveMemoryBefore() {
+ mMemoryBefore = getAvailableMemory();
+ }
+
+ @After
+ public void logMemoryAfter() {
+ long memoryAfter = getAvailableMemory();
+
+ Log.d(TAG, "Available memory: before=" + mMemoryBefore
+ + "MB, after=" + memoryAfter
+ + "MB, delta=" + (memoryAfter - mMemoryBefore) + "MB");
+ }
+
+ /** Method that should be called when a test starts. */
+ public static void onTestStart() {
+ waitForSetupWizardDismissal();
+
+ if (TestStabilityRule.isPresubmit()) {
+ aggressivelyUnlockSysUi();
+ } else {
+ verifyKeyguardInvisible();
+ }
+ }
+
+ private static boolean hasSystemUiObject(String resId) {
+ return getUiDevice().hasObject(
+ By.res(SYSTEMUI_PACKAGE, resId));
+ }
+
+ @NonNull
+ private static UiDevice getUiDevice() {
+ return UiDevice.getInstance(getInstrumentation());
+ }
+
+ private static void aggressivelyUnlockSysUi() {
+ final UiDevice device = getUiDevice();
+ for (int i = 0; i < 10 && hasSystemUiObject("keyguard_status_view"); ++i) {
+ Log.d(TAG, "Before attempting to unlock the phone");
+ try {
+ device.executeShellCommand("input keyevent 82");
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ device.waitForIdle();
+ }
+ Assert.assertTrue("Keyguard still visible",
+ TestHelpers.wait(
+ Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000));
+ Log.d(TAG, "Keyguard is not visible");
+ }
+
+ /** Waits for setup wizard to go away. */
+ private static void waitForSetupWizardDismissal() {
+ if (sFirstTimeWaitingForWizard) {
+ try {
+ getUiDevice().executeShellCommand(
+ "am force-stop com.google.android.setupwizard");
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ final boolean wizardDismissed = TestHelpers.wait(
+ Until.gone(By.pkg("com.google.android.setupwizard").depth(0)),
+ sFirstTimeWaitingForWizard ? 120000 : 0);
+ sFirstTimeWaitingForWizard = false;
+ Assert.assertTrue("Setup wizard is still visible", wizardDismissed);
+ }
+
+ /** Asserts that keyguard is not visible */
+ public static void verifyKeyguardInvisible() {
+ final boolean keyguardAlreadyVisible = sSeenKeyguard;
+
+ sSeenKeyguard = sSeenKeyguard
+ || !TestHelpers.wait(
+ Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000);
+
+ Assert.assertFalse(
+ "Keyguard is visible, which is likely caused by a crash in SysUI, seeing keyguard"
+ + " for the first time = "
+ + !keyguardAlreadyVisible,
+ sSeenKeyguard);
+ }
+
+ @After
+ public void resetFreezeRecentTaskList() {
+ try {
+ mDevice.executeShellCommand("wm reset-freeze-recent-tasks");
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to reset fozen recent tasks list", e);
+ }
+ }
+
+ @After
+ public void verifyLauncherState() {
+ try {
+ // Limits UI tests affecting tests running after them.
+ mDevice.pressHome();
+ mLauncher.waitForLauncherInitialized();
+ if (mLauncherPid != 0) {
+ assertEquals("Launcher crashed, pid mismatch:",
+ mLauncherPid, mLauncher.getPid().intValue());
+ }
+ } finally {
+ mLauncher.onTestFinish();
+ }
+ }
+
+ protected void reinitializeLauncherData() {
+ reinitializeLauncherData(false);
+ }
+
+ protected void reinitializeLauncherData(boolean clearWorkspace) {
+ if (clearWorkspace) {
+ mLauncher.clearLauncherData();
+ } else {
+ mLauncher.reinitializeLauncherData();
+ }
+ mLauncher.waitForLauncherInitialized();
+ }
+
+ public static void startAppFast(String packageName) {
+ startIntent(
+ getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage(
+ packageName),
+ By.pkg(packageName).depth(0),
+ true /* newTask */);
+ }
+
+ public static void startTestActivity(String activityName, String activityLabel) {
+ final String packageName = getAppPackageName();
+ final Intent intent = getInstrumentation().getContext().getPackageManager()
+ .getLaunchIntentForPackage(packageName);
+ intent.setComponent(new ComponentName(packageName,
+ "com.android.launcher3.tests." + activityName));
+ startIntent(intent, By.pkg(packageName).text(activityLabel),
+ false /* newTask */);
+ }
+
+ public static void startTestActivity(int activityNumber) {
+ startTestActivity("Activity" + activityNumber, "TestActivity" + activityNumber);
+ }
+
+ public static void startImeTestActivity() {
+ final String packageName = getAppPackageName();
+ final Intent intent = getInstrumentation().getContext().getPackageManager()
+ .getLaunchIntentForPackage(packageName);
+ intent.setComponent(new ComponentName(packageName,
+ "com.android.launcher3.testcomponent.ImeTestActivity"));
+ startIntent(intent, By.pkg(packageName).text("ImeTestActivity"),
+ false /* newTask */);
+ }
+
+ /** Starts ExcludeFromRecentsTestActivity, which has excludeFromRecents="true". */
+ public static void startExcludeFromRecentsTestActivity() {
+ final String packageName = getAppPackageName();
+ final Intent intent = getInstrumentation().getContext().getPackageManager()
+ .getLaunchIntentForPackage(packageName);
+ intent.setComponent(new ComponentName(packageName,
+ "com.android.launcher3.testcomponent.ExcludeFromRecentsTestActivity"));
+ startIntent(intent, By.pkg(packageName).text("ExcludeFromRecentsTestActivity"),
+ false /* newTask */);
+ }
+
+ private static void startIntent(Intent intent, BySelector selector, boolean newTask) {
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ if (newTask) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ } else {
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+ }
+ getInstrumentation().getTargetContext().startActivity(intent);
+ assertTrue("App didn't start: " + selector,
+ TestHelpers.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
+
+ // Wait for the Launcher to stop.
+ final LauncherInstrumentation launcherInstrumentation = new LauncherInstrumentation();
+ Wait.atMost("Launcher activity didn't stop",
+ () -> !launcherInstrumentation.isLauncherActivityStarted(),
+ launcherInstrumentation, DEFAULT_ACTIVITY_TIMEOUT);
+ }
+
+ public static ActivityInfo resolveSystemAppInfo(String category) {
+ return getInstrumentation().getContext().getPackageManager().resolveActivity(
+ new Intent(Intent.ACTION_MAIN).addCategory(category),
+ PackageManager.MATCH_SYSTEM_ONLY)
+ .activityInfo;
+ }
+
+
+ public static String resolveSystemApp(String category) {
+ return resolveSystemAppInfo(category).packageName;
+ }
+
+ protected HomeAppIcon createShortcutInCenterIfNotExist(String name) {
+ Point dimension = mLauncher.getWorkspace().getIconGridDimensions();
+ return createShortcutIfNotExist(name, dimension.x / 2, dimension.y / 2);
+ }
+
+ protected HomeAppIcon createShortcutIfNotExist(String name, Point cellPosition) {
+ return createShortcutIfNotExist(name, cellPosition.x, cellPosition.y);
+ }
+
+ protected HomeAppIcon createShortcutIfNotExist(String name, int cellX, int cellY) {
+ HomeAppIcon homeAppIcon = mLauncher.getWorkspace().tryGetWorkspaceAppIcon(name);
+ Log.d(ICON_MISSING, "homeAppIcon: " + homeAppIcon + " name: " + name
+ + " cell: " + cellX + ", " + cellY);
+ if (homeAppIcon == null) {
+ HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+ allApps.freeze();
+ try {
+ allApps.getAppIcon(name).dragToWorkspace(cellX, cellY);
+ } finally {
+ allApps.unfreeze();
+ }
+ homeAppIcon = mLauncher.getWorkspace().getWorkspaceAppIcon(name);
+ }
+ return homeAppIcon;
+ }
+
+ protected void commitTransactionAndLoadHome(FavoriteItemsTransaction transaction) {
+ transaction.commit();
+
+ // Launch the home activity
+ UiDevice.getInstance(getInstrumentation()).pressHome();
+ mLauncher.waitForLauncherInitialized();
+ }
+
+ /** Clears all recent tasks */
+ protected void clearAllRecentTasks() {
+ if (!mLauncher.getRecentTasks().isEmpty()) {
+ mLauncher.goHome().switchToOverview().dismissAllTasks();
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
similarity index 78%
rename from tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
rename to tests/src/com/android/launcher3/ui/WorkProfileTest.java
index 33ffd1d..d866a9f 100644
--- a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -17,9 +17,7 @@
import static com.android.launcher3.LauncherPrefs.WORK_EDU_STEP;
import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
-import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
import static com.android.launcher3.util.TestUtil.installDummyAppForUser;
import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
@@ -43,36 +41,39 @@
import com.android.launcher3.allapps.WorkEduCard;
import com.android.launcher3.allapps.WorkPausedCard;
import com.android.launcher3.allapps.WorkProfileManager;
-import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.util.BaseLauncherActivityTest;
import com.android.launcher3.util.TestUtil;
+import com.android.launcher3.util.rule.ScreenRecordRule;
+import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.launcher3.util.rule.TestStabilityRule.Stability;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.IOException;
-import java.util.Objects;
import java.util.function.Predicate;
@LargeTest
@RunWith(AndroidJUnit4.class)
-public class TaplWorkProfileTest extends AbstractLauncherUiTest<Launcher> {
+public class WorkProfileTest extends BaseLauncherActivityTest<Launcher> {
private static final int WORK_PAGE = ActivityAllAppsContainerView.AdapterHolder.WORK;
+ public static final int WAIT_TIME_MS = 30000;
+
+ @Rule
+ public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule();
+ @Rule
+ public TestStabilityRule mTestStabilityRule = new TestStabilityRule();
private int mProfileUserId;
private boolean mWorkProfileSetupSuccessful;
- private final String TAG = "WorkProfileTest";
+ private static final String TAG = "WorkProfileTest";
@Before
- @Override
public void setUp() throws Exception {
- super.setUp();
- String output =
- mDevice.executeShellCommand(
- "pm create-user --profileOf 0 --managed TestProfile");
+ String output = executeShellCommand("pm create-user --profileOf 0 --managed TestProfile");
updateWorkProfileSetupSuccessful("pm create-user", output);
String[] tokens = output.split("\\s+");
@@ -88,36 +89,15 @@
return; // no need to setup launcher since all tests will skip.
}
- mDevice.pressHome();
- waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
- waitForStateTransitionToEnd("Launcher internal state didn't switch to Normal",
- () -> NORMAL);
- waitForResumed("Launcher internal state is still Background");
- mLauncher.getWorkspace().switchToAllApps();
- waitForStateTransitionToEnd("Launcher internal state didn't switch to All Apps",
- () -> ALL_APPS);
+ loadLauncherSync();
+ goToState(ALL_APPS);
+ waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
}
@After
public void removeWorkProfile() throws Exception {
- executeOnLauncherInTearDown(launcher -> {
- if (launcher.getAppsView() == null) {
- return;
- }
- launcher.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
- });
TestUtil.uninstallDummyApp();
-
- mLauncher.runToState(
- () -> {
- try {
- mDevice.executeShellCommand("pm remove-user --wait " + mProfileUserId);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- },
- NORMAL_STATE_ORDINAL,
- "executing pm 'remove-user' command");
+ executeShellCommand("pm remove-user --wait " + mProfileUserId);
}
private void waitForWorkTabSetup() {
@@ -127,7 +107,7 @@
return true;
}
return false;
- }, LauncherInstrumentation.WAIT_TIME_MS);
+ }, WAIT_TIME_MS);
}
@Test
@@ -137,10 +117,10 @@
waitForWorkTabSetup();
waitForLauncherCondition("Personal tab is missing",
launcher -> launcher.getAppsView().isPersonalTabVisible(),
- LauncherInstrumentation.WAIT_TIME_MS);
+ WAIT_TIME_MS);
waitForLauncherCondition("Work tab is missing",
launcher -> launcher.getAppsView().isWorkTabVisible(),
- LauncherInstrumentation.WAIT_TIME_MS);
+ WAIT_TIME_MS);
}
// Staging; will be promoted to presubmit if stable
@@ -156,12 +136,11 @@
WorkProfileManager manager = getFromLauncher(l -> l.getAppsView().getWorkManager());
-
waitForLauncherCondition("work profile initial state check failed", launcher ->
manager.getWorkUtilityView() != null
&& manager.getCurrentState() == WorkProfileManager.STATE_ENABLED
&& manager.getWorkUtilityView().isEnabled(),
- LauncherInstrumentation.WAIT_TIME_MS);
+ WAIT_TIME_MS);
//start work profile toggle OFF test
executeOnLauncher(l -> {
@@ -173,7 +152,7 @@
waitForLauncherCondition("Work profile toggle OFF failed", launcher -> {
manager.reset(); // pulls current state from system
return manager.getCurrentState() == WorkProfileManager.STATE_DISABLED;
- }, LauncherInstrumentation.WAIT_TIME_MS);
+ }, WAIT_TIME_MS);
waitForWorkCard("Work paused card not shown", view -> view instanceof WorkPausedCard);
@@ -188,7 +167,7 @@
waitForLauncherCondition("Work profile toggle ON failed", launcher -> {
manager.reset(); // pulls current state from system
return manager.getCurrentState() == WorkProfileManager.STATE_ENABLED;
- }, LauncherInstrumentation.WAIT_TIME_MS);
+ }, WAIT_TIME_MS);
}
@@ -215,7 +194,7 @@
} finally {
l.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
}
- }, LauncherInstrumentation.WAIT_TIME_MS);
+ }, WAIT_TIME_MS);
}
private void updateWorkProfileSetupSuccessful(String cli, String output) {
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
new file mode 100644
index 0000000..bb645d7
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.ui.widget;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Process;
+import android.view.View;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.celllayout.FavoriteItemsTransaction;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.testcomponent.WidgetConfigActivity;
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.util.BaseLauncherActivityTest;
+import com.android.launcher3.util.BlockingBroadcastReceiver;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.Wait;
+import com.android.launcher3.util.rule.ShellCommandRule;
+import com.android.launcher3.views.OptionsPopupView;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.picker.WidgetsFullSheet;
+import com.android.launcher3.widget.picker.WidgetsListAdapter;
+import com.android.launcher3.widget.picker.WidgetsRecyclerView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test to verify widget configuration is properly shown.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class AddConfigWidgetTest extends BaseLauncherActivityTest<Launcher> {
+
+ @Rule
+ public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
+
+ private LauncherAppWidgetProviderInfo mWidgetInfo;
+ private AppWidgetManager mAppWidgetManager;
+
+ private int mWidgetId;
+
+ @Before
+ public void setUp() throws Exception {
+ mWidgetInfo = TestViewHelpers.findWidgetProvider(true /* hasConfigureScreen */);
+ mAppWidgetManager = AppWidgetManager.getInstance(targetContext());
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testWidgetConfig() throws Throwable {
+ runTest(true);
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testConfigCancelled() throws Throwable {
+ runTest(false);
+ }
+
+ /**
+ * @param acceptConfig accept the config activity
+ */
+ private void runTest(boolean acceptConfig) throws Throwable {
+ new FavoriteItemsTransaction(targetContext()).commit();
+ loadLauncherSync();
+
+ // Add widget to homescreen
+ WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
+ executeOnLauncher(OptionsPopupView::openWidgets);
+ uiDevice.waitForIdle();
+
+ // Select the widget header
+ Context testContext = getInstrumentation().getContext();
+ String packageName = testContext.getPackageName();
+ executeOnLauncher(l -> {
+ WidgetsRecyclerView wrv = WidgetsFullSheet.getWidgetsView(l);
+ WidgetsListAdapter adapter = (WidgetsListAdapter) wrv.getAdapter();
+ int pos = adapter.getItems().indexOf(
+ adapter.getItems().stream()
+ .filter(entry -> packageName.equals(entry.mPkgItem.packageName))
+ .findFirst()
+ .get());
+ wrv.getLayoutManager().scrollToPosition(pos);
+ adapter.onHeaderClicked(true, new PackageUserKey(packageName, Process.myUserHandle()));
+ });
+ uiDevice.waitForIdle();
+
+ View widgetView = getOnceNotNull("Widget not found", l -> searchView(l.getDragLayer(), v ->
+ v instanceof WidgetCell
+ && v.getTag() instanceof PendingAddWidgetInfo pawi
+ && mWidgetInfo.provider.equals(pawi.componentName)));
+ addToWorkspace(widgetView);
+
+ // Widget id for which the config activity was opened
+ mWidgetId = monitor.getWidgetId();
+
+ // Verify that the widget id is valid and bound
+ assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
+ setResult(acceptConfig);
+
+ if (acceptConfig) {
+ getOnceNotNull("Widget was not added", l -> {
+ // Close the resize frame before searching for widget
+ AbstractFloatingView.closeAllOpenViews(l);
+ return l.getWorkspace().getFirstMatch(new WidgetSearchCondition());
+ });
+ assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
+ } else {
+ // Verify that the widget id is deleted.
+ Wait.atMost("", () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null);
+ }
+ }
+
+ private void setResult(boolean success) {
+ getInstrumentation().getTargetContext().sendBroadcast(
+ WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class,
+ success ? "clickOK" : "clickCancel"));
+ uiDevice.waitForIdle();
+ }
+
+ /**
+ * Condition for searching widget id
+ */
+ private class WidgetSearchCondition implements ItemOperator {
+
+ @Override
+ public boolean evaluate(ItemInfo info, View view) {
+ return info instanceof LauncherAppWidgetInfo lawi
+ && lawi.providerName.equals(mWidgetInfo.provider)
+ && lawi.appWidgetId == mWidgetId;
+ }
+ }
+
+ /**
+ * Broadcast receiver for receiving widget config activity status.
+ */
+ private static class WidgetConfigStartupMonitor extends BlockingBroadcastReceiver {
+
+ WidgetConfigStartupMonitor() {
+ super(WidgetConfigActivity.class.getName());
+ }
+
+ public int getWidgetId() throws InterruptedException {
+ Intent intent = blockingGetExtraIntent();
+ assertNotNull("Null EXTRA_INTENT", intent);
+ assertEquals("Intent action is not ACTION_APPWIDGET_CONFIGURE",
+ AppWidgetManager.ACTION_APPWIDGET_CONFIGURE, intent.getAction());
+ int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+ LauncherAppWidgetInfo.NO_ID);
+ assertNotSame("Widget id is NO_ID", widgetId, LauncherAppWidgetInfo.NO_ID);
+ return widgetId;
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
similarity index 77%
rename from tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java
rename to tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index d40d3bc..8846d65 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -1,17 +1,17 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * 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
+ * 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.
+ * 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.ui.widget;
@@ -22,12 +22,12 @@
import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
import static com.android.launcher3.provider.LauncherDbUtils.itemIdMatch;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.TestUtil.getOnUiThread;
+import static com.android.launcher3.util.Wait.atMost;
import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
@@ -36,6 +36,7 @@
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
+import android.text.TextUtils;
import android.widget.RemoteViews;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -49,12 +50,12 @@
import com.android.launcher3.celllayout.FavoriteItemsTransaction;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.pm.InstallSessionHelper;
-import com.android.launcher3.tapl.Widget;
-import com.android.launcher3.tapl.Workspace;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.util.BaseLauncherActivityTest;
import com.android.launcher3.util.rule.ShellCommandRule;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.widget.WidgetManagerHelper;
import org.junit.After;
@@ -66,6 +67,7 @@
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Tests for bind widget flow.
@@ -74,7 +76,7 @@
*/
@LargeTest
@RunWith(AndroidJUnit4.class)
-public class TaplBindWidgetTest extends AbstractLauncherUiTest<Launcher> {
+public class BindWidgetTest extends BaseLauncherActivityTest<Launcher> {
@Rule
public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
@@ -86,11 +88,9 @@
private LauncherModel mModel;
- @Override
@Before
public void setUp() throws Exception {
- super.setUp();
- mModel = LauncherAppState.getInstance(mTargetContext).getModel();
+ mModel = LauncherAppState.getInstance(targetContext()).getModel();
}
@After
@@ -100,7 +100,7 @@
}
if (mSessionId > -1) {
- mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+ targetContext().getPackageManager().getPackageInstaller().abandonSession(mSessionId);
}
}
@@ -121,13 +121,12 @@
LauncherAppWidgetProviderInfo info = addWidgetToScreen(false, false,
item -> item.appWidgetId = -33);
- final Workspace workspace = mLauncher.getWorkspace();
// Item deleted from db
mCursor = queryItem();
assertEquals(0, mCursor.getCount());
// The view does not exist
- assertTrue("Widget exists", workspace.tryGetWidget(info.label, 0) == null);
+ verifyItemEventuallyNull("Widget exists", widgetProvider(info));
}
@Test
@@ -153,18 +152,19 @@
// Widget has a valid Id now.
assertEquals(0, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED))
& FLAG_ID_NOT_VALID);
- assertNotNull(AppWidgetManager.getInstance(mTargetContext)
+ assertNotNull(AppWidgetManager.getInstance(targetContext())
.getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex(
LauncherSettings.Favorites.APPWIDGET_ID))));
// send OPTION_APPWIDGET_RESTORE_COMPLETED
int appWidgetId = mCursor.getInt(
mCursor.getColumnIndex(LauncherSettings.Favorites.APPWIDGET_ID));
- AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mTargetContext);
+ AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(targetContext());
Bundle b = new Bundle();
b.putBoolean(WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED, true);
- RemoteViews remoteViews = new RemoteViews(mTargetPackage, R.layout.appwidget_not_ready);
+ RemoteViews remoteViews = new RemoteViews(
+ targetContext().getPackageName(), R.layout.appwidget_not_ready);
appWidgetManager.updateAppWidgetOptions(appWidgetId, b);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
@@ -174,15 +174,14 @@
WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED));
executeOnLauncher(l -> l.getAppWidgetHolder().startListening());
verifyWidgetPresent(info);
- assertNull(mLauncher.getWorkspace().tryGetPendingWidget(100));
+ verifyItemEventuallyNull("Pending widget exists", pendingWidgetProvider());
}
@Test
public void testPendingWidget_notRestored_removed() {
addPendingItemToScreen(getInvalidWidgetInfo(), FLAG_ID_NOT_VALID | FLAG_PROVIDER_NOT_READY);
- assertTrue("Pending widget exists",
- mLauncher.getWorkspace().tryGetPendingWidget(0) == null);
+ verifyItemEventuallyNull("Pending widget exists", pendingWidgetProvider());
// Item deleted from db
mCursor = queryItem();
assertEquals(0, mCursor.getCount());
@@ -215,7 +214,7 @@
// Create an active installer session
SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
params.setAppPackageName(item.providerName.getPackageName());
- PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller();
+ PackageInstaller installer = targetContext().getPackageManager().getPackageInstaller();
mSessionId = installer.createSession(params);
addPendingItemToScreen(item, FLAG_ID_NOT_VALID | FLAG_PROVIDER_NOT_READY);
@@ -233,34 +232,47 @@
}
private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) {
- final Widget widget = mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT);
- assertTrue("Widget is not present",
- widget != null);
+ getOnceNotNull("Widget is not present", widgetProvider(info));
}
private void verifyPendingWidgetPresent() {
- final Widget widget = mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT);
- assertTrue("Pending widget is not present",
- widget != null);
+ getOnceNotNull("Widget is not present", pendingWidgetProvider());
+ }
+
+ private Function<Launcher, Object> pendingWidgetProvider() {
+ return l -> l.getWorkspace().getFirstMatch(
+ (item, view) -> view instanceof PendingAppWidgetHostView);
+ }
+
+ private Function<Launcher, Object> widgetProvider(LauncherAppWidgetProviderInfo info) {
+ return l -> l.getWorkspace().getFirstMatch((item, view) ->
+ view instanceof LauncherAppWidgetHostView
+ && TextUtils.equals(info.label, view.getContentDescription()));
+ }
+
+ private void verifyItemEventuallyNull(String message, Function<Launcher, Object> provider) {
+ atMost(message, () -> getFromLauncher(provider) == null);
}
private void addPendingItemToScreen(LauncherAppWidgetInfo item, int restoreStatus) {
item.restoreStatus = restoreStatus;
item.screenId = FIRST_SCREEN_ID;
- commitTransactionAndLoadHome(
- new FavoriteItemsTransaction(mTargetContext).addItem(() -> item));
+ new FavoriteItemsTransaction(targetContext()).addItem(() -> item).commit();
+ loadLauncherSync();
}
private LauncherAppWidgetProviderInfo addWidgetToScreen(boolean hasConfigureScreen,
boolean bindWidget, Consumer<LauncherAppWidgetInfo> itemOverride) {
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(hasConfigureScreen);
- commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext)
+ new FavoriteItemsTransaction(targetContext())
.addItem(() -> {
- LauncherAppWidgetInfo item = createWidgetInfo(info, mTargetContext, bindWidget);
+ LauncherAppWidgetInfo item =
+ createWidgetInfo(info, targetContext(), bindWidget);
item.screenId = FIRST_SCREEN_ID;
itemOverride.accept(item);
return item;
- }));
+ }).commit();
+ loadLauncherSync();
return info;
}
@@ -274,13 +286,13 @@
Set<String> activePackage = getOnUiThread(() -> {
Set<String> packages = new HashSet<>();
- InstallSessionHelper.INSTANCE.get(mTargetContext).getActiveSessions()
+ InstallSessionHelper.INSTANCE.get(targetContext()).getActiveSessions()
.keySet().forEach(packageUserKey -> packages.add(packageUserKey.mPackageName));
return packages;
});
while (true) {
try {
- mTargetContext.getPackageManager().getPackageInfo(
+ targetContext().getPackageManager().getPackageInfo(
pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (Exception e) {
if (!activePackage.contains(pkg)) {
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
similarity index 61%
rename from tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java
rename to tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 35c7cab..2fb7987 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -1,34 +1,41 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * 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
+ * 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.
+ * 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.ui.widget;
import static android.app.PendingIntent.FLAG_MUTABLE;
import static android.app.PendingIntent.FLAG_ONE_SHOT;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
+import static java.util.regex.Pattern.CASE_INSENSITIVE;
+
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
+import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -37,14 +44,13 @@
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.tapl.AddToHomeScreenPrompt;
import com.android.launcher3.testcomponent.AppWidgetNoConfig;
import com.android.launcher3.testcomponent.AppWidgetWithConfig;
import com.android.launcher3.testcomponent.RequestPinItemActivity;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.BaseLauncherActivityTest;
+import com.android.launcher3.util.BlockingBroadcastReceiver;
import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
-import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.Wait.Condition;
+import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.rule.ShellCommandRule;
import org.junit.Before;
@@ -53,25 +59,27 @@
import org.junit.runner.RunWith;
import java.util.UUID;
+import java.util.regex.Pattern;
/**
* Test to verify pin item request flow.
*/
@LargeTest
@RunWith(AndroidJUnit4.class)
-public class TaplRequestPinItemTest extends AbstractLauncherUiTest<Launcher> {
+public class RequestPinItemTest extends BaseLauncherActivityTest<Launcher> {
@Rule
public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
+ @Rule
+ public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher();
+
private String mCallbackAction;
private String mShortcutId;
private int mAppWidgetId;
- @Override
@Before
public void setUp() throws Exception {
- super.setUp();
mCallbackAction = UUID.randomUUID().toString();
mShortcutId = UUID.randomUUID().toString();
}
@@ -81,9 +89,9 @@
@Test
public void testPinWidgetNoConfig() throws Throwable {
- runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo &&
- ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
- ((LauncherAppWidgetInfo) info).providerName.getClassName()
+ runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo
+ && ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId
+ && ((LauncherAppWidgetInfo) info).providerName.getClassName()
.equals(AppWidgetNoConfig.class.getName()));
}
@@ -94,18 +102,18 @@
RequestPinItemActivity.class, "setRemoteViewColor").putExtra(
RequestPinItemActivity.EXTRA_PARAM + "0", Color.RED);
- runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo &&
- ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
- ((LauncherAppWidgetInfo) info).providerName.getClassName()
+ runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo
+ && ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId
+ && ((LauncherAppWidgetInfo) info).providerName.getClassName()
.equals(AppWidgetNoConfig.class.getName()), command);
}
@Test
public void testPinWidgetWithConfig() throws Throwable {
runTest("pinWidgetWithConfig", true,
- (info, view) -> info instanceof LauncherAppWidgetInfo &&
- ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
- ((LauncherAppWidgetInfo) info).providerName.getClassName()
+ (info, view) -> info instanceof LauncherAppWidgetInfo
+ && ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId
+ && ((LauncherAppWidgetInfo) info).providerName.getClassName()
.equals(AppWidgetWithConfig.class.getName()));
}
@@ -119,47 +127,48 @@
runTest("pinShortcut", false, new ItemOperator() {
@Override
public boolean evaluate(ItemInfo info, View view) {
- return info instanceof WorkspaceItemInfo &&
- info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
- ShortcutKey.fromItemInfo(info).getId().equals(mShortcutId);
+ return info instanceof WorkspaceItemInfo
+ && info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT
+ && ShortcutKey.fromItemInfo(info).getId().equals(mShortcutId);
}
}, command);
}
private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher,
Intent... commandIntents) throws Throwable {
- commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
+ new FavoriteItemsTransaction(targetContext()).commit();
+ loadLauncherSync();
// Open Pin item activity
BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver(
RequestPinItemActivity.class.getName());
- mLauncher.
- getWorkspace().
- switchToAllApps().
- getAppIcon("Test Pin Item").
- launch(getAppPackageName());
+ Context testContext = getInstrumentation().getContext();
+ startAppFast(
+ testContext.getPackageName(),
+ new Intent(testContext, RequestPinItemActivity.class));
assertNotNull(openMonitor.blockingGetExtraIntent());
// Set callback
- PendingIntent callback = PendingIntent.getBroadcast(mTargetContext, 0,
- new Intent(mCallbackAction).setPackage(mTargetContext.getPackageName()),
+ PendingIntent callback = PendingIntent.getBroadcast(targetContext(), 0,
+ new Intent(mCallbackAction).setPackage(targetContext().getPackageName()),
FLAG_ONE_SHOT | FLAG_MUTABLE);
- mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
+ targetContext().sendBroadcast(RequestPinItemActivity.getCommandIntent(
RequestPinItemActivity.class, "setCallback").putExtra(
RequestPinItemActivity.EXTRA_PARAM + "0", callback));
for (Intent command : commandIntents) {
- mTargetContext.sendBroadcast(command);
+ targetContext().sendBroadcast(command);
}
// call the requested method to start the flow
- mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
+ targetContext().sendBroadcast(RequestPinItemActivity.getCommandIntent(
RequestPinItemActivity.class, activityMethod));
- final AddToHomeScreenPrompt addToHomeScreenPrompt = mLauncher.getAddToHomeScreenPrompt();
// Accept confirmation:
BlockingBroadcastReceiver resultReceiver = new BlockingBroadcastReceiver(mCallbackAction);
- addToHomeScreenPrompt.addAutomatically();
+ BySelector selector = By.text(Pattern.compile("^Add to home screen$", CASE_INSENSITIVE))
+ .pkg(targetContext().getPackageName());
+ uiDevice.wait(device -> device.findObject(selector), TestUtil.DEFAULT_UI_TIMEOUT).click();
Intent result = resultReceiver.blockingGetIntent();
assertNotNull(result);
mAppWidgetId = result.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
@@ -167,29 +176,9 @@
assertNotSame(-1, mAppWidgetId);
}
- // Go back to home
- mLauncher.goHome();
- Wait.atMost("", new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT,
- mLauncher);
- }
-
- /**
- * Condition for for an item
- */
- private class ItemSearchCondition implements Condition {
-
- private final ItemOperator mOp;
-
- ItemSearchCondition(ItemOperator op) {
- mOp = op;
- }
-
- @Override
- public boolean isTrue() throws Throwable {
- return mMainThreadExecutor.submit(() -> {
- Launcher l = Launcher.ACTIVITY_TRACKER.getCreatedContext();
- return l != null && l.getWorkspace().getFirstMatch(mOp) != null;
- }).get();
- }
+ // Reload activity, so that the activity is focused
+ closeCurrentActivity();
+ loadLauncherSync();
+ getOnceNotNull("", l -> l.getWorkspace().getFirstMatch(itemMatcher));
}
}
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
deleted file mode 100644
index 7ff4f22..0000000
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.android.launcher3.ui.widget;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
-
-import android.appwidget.AppWidgetManager;
-import android.content.Intent;
-import android.view.View;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.celllayout.FavoriteItemsTransaction;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.testcomponent.WidgetConfigActivity;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
-import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
-import com.android.launcher3.ui.TestViewHelpers;
-import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.ShellCommandRule;
-import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test to verify widget configuration is properly shown.
- */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class TaplAddConfigWidgetTest extends AbstractLauncherUiTest<Launcher> {
-
- @Rule
- public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
-
- private LauncherAppWidgetProviderInfo mWidgetInfo;
- private AppWidgetManager mAppWidgetManager;
-
- private int mWidgetId;
-
- @Override
- @Before
- public void setUp() throws Exception {
- super.setUp();
- mWidgetInfo = TestViewHelpers.findWidgetProvider(true /* hasConfigureScreen */);
- mAppWidgetManager = AppWidgetManager.getInstance(mTargetContext);
- }
-
- @Test
- @PortraitLandscape
- public void testWidgetConfig() throws Throwable {
- runTest(true);
- }
-
- @Test
- @PortraitLandscape
- public void testConfigCancelled() throws Throwable {
- runTest(false);
- }
-
-
- /**
- * @param acceptConfig accept the config activity
- */
- private void runTest(boolean acceptConfig) throws Throwable {
- commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
-
- // Drag widget to homescreen
- WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
- mLauncher.getWorkspace()
- .openAllWidgets()
- .getWidget(mWidgetInfo.getLabel())
- .dragToWorkspace(true, false);
- // Widget id for which the config activity was opened
- mWidgetId = monitor.getWidgetId();
-
- // Verify that the widget id is valid and bound
- assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
-
- setResultAndWaitForAnimation(acceptConfig);
- if (acceptConfig) {
- Wait.atMost("", new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
- assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
- } else {
- // Verify that the widget id is deleted.
- Wait.atMost("", () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null,
- DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
- }
- }
-
- 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
- */
- private class WidgetSearchCondition implements Wait.Condition, ItemOperator {
-
- @Override
- public boolean isTrue() throws Throwable {
- return mMainThreadExecutor.submit(() -> {
- Launcher l = Launcher.ACTIVITY_TRACKER.getCreatedContext();
- return l != null && l.getWorkspace().getFirstMatch(this) != null;
- }).get();
- }
-
- @Override
- public boolean evaluate(ItemInfo info, View view) {
- return info instanceof LauncherAppWidgetInfo
- && ((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
- mWidgetInfo.provider.getClassName())
- && ((LauncherAppWidgetInfo) info).appWidgetId == mWidgetId;
- }
- }
-
- /**
- * Broadcast receiver for receiving widget config activity status.
- */
- private class WidgetConfigStartupMonitor extends BlockingBroadcastReceiver {
-
- public WidgetConfigStartupMonitor() {
- super(WidgetConfigActivity.class.getName());
- }
-
- public int getWidgetId() throws InterruptedException {
- Intent intent = blockingGetExtraIntent();
- assertNotNull("Null EXTRA_INTENT", intent);
- assertEquals("Intent action is not ACTION_APPWIDGET_CONFIGURE",
- AppWidgetManager.ACTION_APPWIDGET_CONFIGURE, intent.getAction());
- int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
- LauncherAppWidgetInfo.NO_ID);
- assertNotSame("Widget id is NO_ID", widgetId, LauncherAppWidgetInfo.NO_ID);
- return widgetId;
- }
- }
-}
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
index 9a2147a..460ffc4 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
@@ -29,6 +29,7 @@
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
@@ -68,7 +69,7 @@
resizeFrame.dismiss();
final Widget widget = mLauncher.getWorkspace().tryGetWidget(widgetInfo.label,
- DEFAULT_UI_TIMEOUT);
+ TestUtil.DEFAULT_UI_TIMEOUT);
assertNotNull("Widget not found on the workspace", widget);
widget.launch(getAppPackageName());
mLauncher.disableDebugTracing(); // b/289161193
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java b/tests/src/com/android/launcher3/ui/widget/WidgetPickerTest.java
similarity index 68%
rename from tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java
rename to tests/src/com/android/launcher3/ui/widget/WidgetPickerTest.java
index 19c5850..caad1d9 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/WidgetPickerTest.java
@@ -22,24 +22,30 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
-import com.android.launcher3.tapl.Widgets;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.util.BaseLauncherActivityTest;
+import com.android.launcher3.util.rule.ScreenRecordRule;
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
+import com.android.launcher3.views.OptionsPopupView;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
import com.android.launcher3.widget.picker.WidgetsRecyclerView;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
/**
- * This test run in both Out of process (Oop) and in-process (Ipc).
* Make sure the basic interactions with the WidgetPicker works.
*/
@MediumTest
@RunWith(AndroidJUnit4.class)
-public class TaplWidgetPickerTest extends AbstractLauncherUiTest<Launcher> {
+public class WidgetPickerTest extends BaseLauncherActivityTest<Launcher> {
+
+ @Rule
+ public TestRule screenRecordRule = new ScreenRecordRule();
private WidgetsRecyclerView getWidgetsView(Launcher launcher) {
return WidgetsFullSheet.getWidgetsView(launcher);
@@ -56,30 +62,21 @@
@ScreenRecord
@PortraitLandscape
public void testWidgets() {
- mLauncher.goHome();
+ loadLauncherSync();
// Test opening widgets.
executeOnLauncher(launcher ->
assertTrue("Widgets is initially opened", getWidgetsView(launcher) == null));
- Widgets widgets = mLauncher.getWorkspace().openAllWidgets();
- assertNotNull("openAllWidgets() returned null", widgets);
- widgets = mLauncher.getAllWidgets();
+ assertNotNull("openAllWidgets() returned null",
+ getFromLauncher(OptionsPopupView::openWidgets));
+ WidgetsRecyclerView widgets = getFromLauncher(this::getWidgetsView);
assertNotNull("getAllWidgets() returned null", widgets);
- executeOnLauncher(launcher ->
- assertTrue("Widgets is not shown", getWidgetsView(launcher).isShown()));
+ executeOnLauncher(launcher -> assertTrue("Widgets is not shown", widgets.isShown()));
executeOnLauncher(launcher -> assertEquals("Widgets is scrolled upon opening",
0, getWidgetsScroll(launcher)));
- // Test flinging widgets.
- widgets.flingForward();
- Integer flingForwardY = getFromLauncher(launcher -> getWidgetsScroll(launcher));
- executeOnLauncher(launcher -> assertTrue("Flinging forward didn't scroll widgets",
- flingForwardY > 0));
+ executeOnLauncher(AbstractFloatingView::closeAllOpenViews);
+ uiDevice.waitForIdle();
- widgets.flingBackward();
- executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets",
- getWidgetsScroll(launcher) < flingForwardY));
-
- mLauncher.goHome();
waitForLauncherCondition("Widgets were not closed",
launcher -> getWidgetsView(launcher) == null);
}
diff --git a/tests/src/com/android/launcher3/ui/workspace/TaplThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplThemeIconsTest.java
deleted file mode 100644
index d653317..0000000
--- a/tests/src/com/android/launcher3/ui/workspace/TaplThemeIconsTest.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.ui.workspace;
-
-import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.net.Uri;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.test.filters.LargeTest;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.icons.ThemedIconDrawable;
-import com.android.launcher3.tapl.HomeAllApps;
-import com.android.launcher3.tapl.HomeAppIcon;
-import com.android.launcher3.tapl.HomeAppIconMenuItem;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
-import com.android.launcher3.util.Executors;
-
-import org.junit.Test;
-
-import java.util.ArrayDeque;
-import java.util.Queue;
-
-/**
- * Tests for theme icon support in Launcher
- *
- * Note running these tests will clear the workspace on the device.
- */
-@LargeTest
-public class TaplThemeIconsTest extends AbstractLauncherUiTest<Launcher> {
-
- private static final String APP_NAME = "IconThemedActivity";
- private static final String SHORTCUT_NAME = "Shortcut 1";
-
- @Test
- public void testIconWithoutTheme() throws Exception {
- setThemeEnabled(false);
- initialize(this);
-
- HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
- allApps.freeze();
-
- try {
- HomeAppIcon icon = allApps.getAppIcon(APP_NAME);
- executeOnLauncher(l -> verifyIconTheme(APP_NAME, l.getAppsView(), false));
- icon.dragToWorkspace(false, false);
- executeOnLauncher(l -> verifyIconTheme(APP_NAME, l.getWorkspace(), false));
- } finally {
- allApps.unfreeze();
- }
- }
-
- @Test
- public void testShortcutIconWithoutTheme() throws Exception {
- setThemeEnabled(false);
- initialize(this);
-
- HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
- allApps.freeze();
-
- try {
- HomeAppIcon icon = allApps.getAppIcon(TEST_APP_NAME);
- HomeAppIconMenuItem shortcutItem =
- (HomeAppIconMenuItem) icon.openDeepShortcutMenu().getMenuItem(SHORTCUT_NAME);
- shortcutItem.dragToWorkspace(false, false);
- executeOnLauncher(l -> verifyIconTheme(SHORTCUT_NAME, l.getWorkspace(), false));
- } finally {
- allApps.unfreeze();
- }
- }
-
- @Test
- public void testIconWithTheme() throws Exception {
- setThemeEnabled(true);
- initialize(this);
-
- HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
- allApps.freeze();
-
- try {
- HomeAppIcon icon = allApps.getAppIcon(APP_NAME);
- executeOnLauncher(l -> verifyIconTheme(APP_NAME, l.getAppsView(), false));
- icon.dragToWorkspace(false, false);
- executeOnLauncher(l -> verifyIconTheme(APP_NAME, l.getWorkspace(), true));
- } finally {
- allApps.unfreeze();
- }
- }
-
- @Test
- public void testShortcutIconWithTheme() throws Exception {
- setThemeEnabled(true);
- initialize(this);
-
- HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
- allApps.freeze();
-
- try {
- HomeAppIcon icon = allApps.getAppIcon(TEST_APP_NAME);
- HomeAppIconMenuItem shortcutItem =
- (HomeAppIconMenuItem) icon.openDeepShortcutMenu().getMenuItem(SHORTCUT_NAME);
- shortcutItem.dragToWorkspace(false, false);
- executeOnLauncher(l -> verifyIconTheme(SHORTCUT_NAME, l.getWorkspace(), true));
- } finally {
- allApps.unfreeze();
- }
- }
-
- private void verifyIconTheme(String title, ViewGroup parent, boolean isThemed) {
- // Wait for Launcher model to be completed
- try {
- Executors.MODEL_EXECUTOR.submit(() -> { }).get();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
-
- // Find the app icon
- Queue<View> viewQueue = new ArrayDeque<>();
- viewQueue.add(parent);
- BubbleTextView icon = null;
- while (!viewQueue.isEmpty()) {
- View view = viewQueue.poll();
- if (view instanceof ViewGroup) {
- parent = (ViewGroup) view;
- for (int i = parent.getChildCount() - 1; i >= 0; i--) {
- viewQueue.add(parent.getChildAt(i));
- }
- } else if (view instanceof BubbleTextView btv) {
- if (title.equals(btv.getContentDescription().toString())) {
- icon = btv;
- break;
- }
- }
- }
-
- assertNotNull(icon.getIcon());
- assertEquals(isThemed, icon.getIcon() instanceof ThemedIconDrawable);
- }
-
- private void setThemeEnabled(boolean isEnabled) throws Exception {
- Uri uri = new Uri.Builder()
- .scheme(ContentResolver.SCHEME_CONTENT)
- .authority(mTargetPackage + ".grid_control")
- .appendPath("set_icon_themed")
- .build();
- ContentValues values = new ContentValues();
- values.put("boolean_value", isEnabled);
- try (ContentProviderClient client = mTargetContext.getContentResolver()
- .acquireContentProviderClient(uri)) {
- int result = client.update(uri, values, null);
- assertTrue(result > 0);
- }
- }
-}
diff --git a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
new file mode 100644
index 0000000..c623513
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.ui.workspace;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ACTION_POPUP;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.view.ViewGroup;
+
+import androidx.test.filters.LargeTest;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.allapps.AllAppsRecyclerView;
+import com.android.launcher3.celllayout.FavoriteItemsTransaction;
+import com.android.launcher3.icons.ThemedIconDrawable;
+import com.android.launcher3.popup.ArrowPopup;
+import com.android.launcher3.util.BaseLauncherActivityTest;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.TestUtil;
+
+import org.junit.Test;
+
+/**
+ * Tests for theme icon support in Launcher
+ *
+ * Note running these tests will clear the workspace on the device.
+ */
+@LargeTest
+public class ThemeIconsTest extends BaseLauncherActivityTest<Launcher> {
+
+ private static final String APP_NAME = "IconThemedActivity";
+ private static final String SHORTCUT_NAME = "Shortcut 1";
+
+ @Test
+ public void testIconWithoutTheme() throws Exception {
+ setThemeEnabled(false);
+ new FavoriteItemsTransaction(targetContext()).commit();
+ loadLauncherSync();
+ goToState(LauncherState.ALL_APPS);
+ freezeAllApps();
+
+ scrollToAppIcon(APP_NAME);
+ BubbleTextView btv = getFromLauncher(
+ l -> verifyIconTheme(APP_NAME, l.getAppsView(), false));
+ addToWorkspace(btv);
+ executeOnLauncher(l -> verifyIconTheme(APP_NAME, l.getWorkspace(), false));
+ }
+
+ @Test
+ public void testShortcutIconWithoutTheme() throws Exception {
+ setThemeEnabled(false);
+ new FavoriteItemsTransaction(targetContext()).commit();
+ loadLauncherSync();
+ goToState(LauncherState.ALL_APPS);
+ freezeAllApps();
+
+ scrollToAppIcon(TEST_APP_NAME);
+ BubbleTextView btv = getFromLauncher(l -> findBtv(TEST_APP_NAME, l.getAppsView()));
+ TestUtil.runOnExecutorSync(MAIN_EXECUTOR, btv::performLongClick);
+
+ BubbleTextView menuItem = getOnceNotNull("Popup menu not open", l ->
+ (AbstractFloatingView.getOpenView(l, TYPE_ACTION_POPUP) instanceof ArrowPopup ap)
+ ? findBtv(SHORTCUT_NAME, ap) : null);
+ addToWorkspace(menuItem);
+ executeOnLauncher(l -> verifyIconTheme(SHORTCUT_NAME, l.getWorkspace(), false));
+ }
+
+ @Test
+ public void testIconWithTheme() throws Exception {
+ setThemeEnabled(true);
+ new FavoriteItemsTransaction(targetContext()).commit();
+ loadLauncherSync();
+ goToState(LauncherState.ALL_APPS);
+ freezeAllApps();
+
+ scrollToAppIcon(APP_NAME);
+ BubbleTextView btv = getFromLauncher(l ->
+ verifyIconTheme(APP_NAME, l.getAppsView(), false));
+ addToWorkspace(btv);
+ executeOnLauncher(l -> verifyIconTheme(APP_NAME, l.getWorkspace(), true));
+ }
+
+ @Test
+ public void testShortcutIconWithTheme() throws Exception {
+ setThemeEnabled(true);
+ loadLauncherSync();
+ goToState(LauncherState.ALL_APPS);
+ freezeAllApps();
+
+ scrollToAppIcon(TEST_APP_NAME);
+ BubbleTextView btv = getFromLauncher(l -> findBtv(TEST_APP_NAME, l.getAppsView()));
+ TestUtil.runOnExecutorSync(MAIN_EXECUTOR, btv::performLongClick);
+
+ BubbleTextView menuItem = getOnceNotNull("Popup menu not open", l ->
+ (AbstractFloatingView.getOpenView(l, TYPE_ACTION_POPUP) instanceof ArrowPopup ap)
+ ? findBtv(SHORTCUT_NAME, ap) : null);
+ addToWorkspace(menuItem);
+ executeOnLauncher(l -> verifyIconTheme(SHORTCUT_NAME, l.getWorkspace(), true));
+ }
+
+ private BubbleTextView findBtv(String title, ViewGroup parent) {
+ // Wait for Launcher model to be completed
+ try {
+ Executors.MODEL_EXECUTOR.submit(() -> { }).get();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return (BubbleTextView) searchView(parent, v ->
+ v instanceof BubbleTextView btv
+ && btv.getContentDescription() != null
+ && title.equals(btv.getContentDescription().toString()));
+ }
+
+ private BubbleTextView verifyIconTheme(String title, ViewGroup parent, boolean isThemed) {
+ BubbleTextView icon = findBtv(title, parent);
+ assertNotNull(icon.getIcon());
+ assertEquals(isThemed, icon.getIcon() instanceof ThemedIconDrawable);
+ return icon;
+ }
+
+ private void setThemeEnabled(boolean isEnabled) throws Exception {
+ Uri uri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(targetContext().getPackageName() + ".grid_control")
+ .appendPath("set_icon_themed")
+ .build();
+ ContentValues values = new ContentValues();
+ values.put("boolean_value", isEnabled);
+ try (ContentProviderClient client = targetContext().getContentResolver()
+ .acquireContentProviderClient(uri)) {
+ int result = client.update(uri, values, null);
+ assertTrue(result > 0);
+ }
+ }
+
+ private void scrollToAppIcon(String appName) {
+ executeOnLauncher(l -> {
+ l.hideKeyboard();
+ AllAppsRecyclerView rv = l.getAppsView().getActiveRecyclerView();
+ int pos = rv.getApps().getAdapterItems().indexOf(rv.getApps().getAdapterItems().stream()
+ .filter(i -> i.itemInfo != null && appName.equals(i.itemInfo.title.toString()))
+ .findFirst()
+ .get());
+ rv.getLayoutManager().scrollToPosition(pos);
+ });
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/BaseLauncherActivityTest.kt b/tests/src/com/android/launcher3/util/BaseLauncherActivityTest.kt
new file mode 100644
index 0000000..6446592
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/BaseLauncherActivityTest.kt
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.Intent
+import android.os.SystemClock
+import android.view.InputDevice
+import android.view.KeyCharacterMap
+import android.view.KeyEvent
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.children
+import androidx.lifecycle.Lifecycle.State.RESUMED
+import androidx.test.core.app.ActivityScenario
+import androidx.test.core.app.ActivityScenario.ActivityAction
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.Launcher
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.LauncherState
+import com.android.launcher3.R
+import com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST
+import com.android.launcher3.tapl.TestHelpers
+import com.android.launcher3.util.ModelTestExtensions.loadModelSync
+import com.android.launcher3.util.Wait.atMost
+import java.util.function.Function
+import java.util.function.Predicate
+import java.util.function.Supplier
+import org.junit.After
+
+/**
+ * Base class for tests which use Launcher activity with some utility methods.
+ *
+ * This should instead be a rule, but is kept as a base class for easier migration from TAPL
+ */
+open class BaseLauncherActivityTest<LAUNCHER_TYPE : Launcher> {
+
+ private var currentScenario: ActivityScenario<LAUNCHER_TYPE>? = null
+
+ val scenario: ActivityScenario<LAUNCHER_TYPE>
+ get() =
+ currentScenario
+ ?: ActivityScenario.launch<LAUNCHER_TYPE>(
+ TestHelpers.getHomeIntentInPackage(targetContext()),
+ null,
+ )
+ .also { currentScenario = it }
+
+ @JvmField val uiDevice = UiDevice.getInstance(getInstrumentation())
+
+ @After
+ fun closeCurrentActivity() {
+ currentScenario?.close()
+ currentScenario = null
+ }
+
+ protected fun loadLauncherSync() {
+ LauncherAppState.getInstance(targetContext()).model.loadModelSync()
+ scenario.moveToState(RESUMED)
+ }
+
+ protected fun targetContext(): Context = getInstrumentation().targetContext
+
+ protected fun goToState(state: LauncherState) {
+ executeOnLauncher { it.stateManager.goToState(state, 0) }
+ UiDevice.getInstance(getInstrumentation()).waitForIdle()
+ }
+
+ protected fun executeOnLauncher(f: ActivityAction<LAUNCHER_TYPE>) = scenario.onActivity(f)
+
+ protected fun <T> getFromLauncher(f: Function<in LAUNCHER_TYPE, out T?>): T? {
+ var result: T? = null
+ executeOnLauncher { result = f.apply(it) }
+ return result
+ }
+
+ protected fun isInState(state: Supplier<LauncherState>): Boolean =
+ getFromLauncher { it.stateManager.state == state.get() }!!
+
+ protected fun waitForState(message: String, state: Supplier<LauncherState>) =
+ waitForLauncherCondition(message) { it.stateManager.currentStableState === state.get() }
+
+ protected fun waitForLauncherCondition(
+ message: String,
+ condition: Function<LAUNCHER_TYPE, Boolean>,
+ ) = atMost(message, { getFromLauncher(condition)!! })
+
+ protected fun waitForLauncherCondition(
+ message: String,
+ condition: Function<LAUNCHER_TYPE, Boolean>,
+ timeout: Long,
+ ) = atMost(message, { getFromLauncher(condition)!! }, null, timeout)
+
+ protected fun <T> getOnceNotNull(message: String, f: Function<LAUNCHER_TYPE, T?>): T? {
+ var output: T? = null
+ atMost(
+ message,
+ {
+ val fromLauncher = getFromLauncher<T>(f)
+ output = fromLauncher
+ fromLauncher != null
+ },
+ )
+ return output
+ }
+
+ protected fun getAllAppsScroll(launcher: LAUNCHER_TYPE) =
+ launcher.appsView.activeRecyclerView.computeVerticalScrollOffset()
+
+ @JvmOverloads
+ protected fun injectKeyEvent(keyCode: Int, actionDown: Boolean, metaState: Int = 0) {
+ val eventTime = SystemClock.uptimeMillis()
+ val event =
+ KeyEvent.obtain(
+ eventTime,
+ eventTime,
+ if (actionDown) KeyEvent.ACTION_DOWN else MotionEvent.ACTION_UP,
+ keyCode,
+ /* repeat= */ 0,
+ metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD,
+ /* scancode= */ 0,
+ /* flags= */ 0,
+ InputDevice.SOURCE_KEYBOARD,
+ /* characters =*/ null,
+ )
+ executeOnLauncher { it.dispatchKeyEvent(event) }
+ event.recycle()
+ }
+
+ @JvmOverloads
+ fun startAppFast(
+ packageName: String,
+ intent: Intent = targetContext().packageManager.getLaunchIntentForPackage(packageName)!!,
+ ) {
+ intent.addCategory(Intent.CATEGORY_LAUNCHER)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ targetContext().startActivity(intent)
+ uiDevice.waitForIdle()
+ }
+
+ fun freezeAllApps() = executeOnLauncher {
+ it.appsView.appsStore.enableDeferUpdates(DEFER_UPDATES_TEST)
+ }
+
+ fun executeShellCommand(cmd: String) = uiDevice.executeShellCommand(cmd)
+
+ fun addToWorkspace(view: View) {
+ TestUtil.runOnExecutorSync(Executors.MAIN_EXECUTOR) {
+ view.accessibilityDelegate.performAccessibilityAction(
+ view,
+ R.id.action_add_to_workspace,
+ null,
+ )
+ }
+ UiDevice.getInstance(getInstrumentation()).waitForIdle()
+ }
+
+ fun ViewGroup.searchView(filter: Predicate<View>): View? {
+ if (filter.test(this)) return this
+ for (child in children) {
+ if (filter.test(child)) return child
+ if (child is ViewGroup)
+ child.searchView(filter)?.let {
+ return it
+ }
+ }
+ return null
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/BlockingBroadcastReceiver.kt b/tests/src/com/android/launcher3/util/BlockingBroadcastReceiver.kt
new file mode 100644
index 0000000..20881d1
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/BlockingBroadcastReceiver.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Parcelable
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit.SECONDS
+
+private const val DEFAULT_BROADCAST_TIMEOUT_SECS: Long = 10
+
+/** Broadcast receiver which blocks until the result is received. */
+open class BlockingBroadcastReceiver(action: String) : BroadcastReceiver() {
+
+ val value = CompletableFuture<Intent>()
+
+ init {
+ getInstrumentation()
+ .targetContext
+ .registerReceiver(this, IntentFilter(action), Context.RECEIVER_EXPORTED)
+ }
+
+ override fun onReceive(context: Context, intent: Intent) {
+ value.complete(intent)
+ }
+
+ @Throws(InterruptedException::class)
+ fun blockingGetIntent(): Intent =
+ value.get(DEFAULT_BROADCAST_TIMEOUT_SECS, SECONDS).also {
+ getInstrumentation().targetContext.unregisterReceiver(this)
+ }
+
+ @Throws(InterruptedException::class)
+ fun blockingGetExtraIntent(): Intent? =
+ blockingGetIntent().getParcelableExtra<Parcelable>(Intent.EXTRA_INTENT) as Intent?
+}
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
deleted file mode 100644
index 50bc32e..0000000
--- a/tests/src/com/android/launcher3/util/Wait.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package com.android.launcher3.util;
-
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.launcher3.tapl.LauncherInstrumentation;
-
-import org.junit.Assert;
-
-import java.util.function.Supplier;
-
-/**
- * A utility class for waiting for a condition to be true.
- */
-public class Wait {
-
- private static final long DEFAULT_SLEEP_MS = 200;
-
- public static void atMost(String message, Condition condition, long timeout,
- LauncherInstrumentation launcher) {
- atMost(() -> message, condition, timeout, DEFAULT_SLEEP_MS, launcher);
- }
-
- public static void atMost(Supplier<String> message, Condition condition, long timeout,
- LauncherInstrumentation launcher) {
- atMost(message, condition, timeout, DEFAULT_SLEEP_MS, launcher);
- }
-
- public static void atMost(Supplier<String> message, Condition condition, long timeout,
- long sleepMillis,
- LauncherInstrumentation launcher) {
- final long startTime = SystemClock.uptimeMillis();
- long endTime = startTime + timeout;
- Log.d("Wait", "atMost: " + startTime + " - " + endTime);
- while (SystemClock.uptimeMillis() < endTime) {
- try {
- if (condition.isTrue()) {
- return;
- }
- } catch (Throwable t) {
- throw new RuntimeException(t);
- }
- SystemClock.sleep(sleepMillis);
- }
-
- // Check once more before returning false.
- try {
- if (condition.isTrue()) {
- return;
- }
- } catch (Throwable t) {
- throw new RuntimeException(t);
- }
- Log.d("Wait", "atMost: timed out: " + SystemClock.uptimeMillis());
- launcher.checkForAnomaly(false, false);
- Assert.fail(message.get());
- }
-
- /**
- * Interface representing a generic condition
- */
- public interface Condition {
-
- boolean isTrue() throws Throwable;
- }
-}
diff --git a/tests/src/com/android/launcher3/util/Wait.kt b/tests/src/com/android/launcher3/util/Wait.kt
new file mode 100644
index 0000000..1e5af54
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/Wait.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util
+
+import android.os.SystemClock
+import android.util.Log
+import com.android.launcher3.tapl.LauncherInstrumentation
+import java.util.function.Supplier
+import org.junit.Assert
+
+/** A utility class for waiting for a condition to be true. */
+object Wait {
+ private const val DEFAULT_SLEEP_MS: Long = 200
+
+ @JvmStatic
+ @JvmOverloads
+ fun atMost(
+ message: String,
+ condition: Condition,
+ launcherInstrumentation: LauncherInstrumentation? = null,
+ timeout: Long = TestUtil.DEFAULT_UI_TIMEOUT,
+ ) {
+ atMost({ message }, condition, launcherInstrumentation, timeout)
+ }
+
+ @JvmStatic
+ @JvmOverloads
+ fun atMost(
+ message: Supplier<String>,
+ condition: Condition,
+ launcherInstrumentation: LauncherInstrumentation? = null,
+ timeout: Long = TestUtil.DEFAULT_UI_TIMEOUT,
+ ) {
+ val startTime = SystemClock.uptimeMillis()
+ val endTime = startTime + timeout
+ Log.d("Wait", "atMost: $startTime - $endTime")
+ while (SystemClock.uptimeMillis() < endTime) {
+ try {
+ if (condition.isTrue()) {
+ return
+ }
+ } catch (t: Throwable) {
+ throw RuntimeException(t)
+ }
+ SystemClock.sleep(DEFAULT_SLEEP_MS)
+ }
+
+ // Check once more before returning false.
+ try {
+ if (condition.isTrue()) {
+ return
+ }
+ } catch (t: Throwable) {
+ throw RuntimeException(t)
+ }
+ Log.d("Wait", "atMost: timed out: " + SystemClock.uptimeMillis())
+ launcherInstrumentation?.checkForAnomaly(false, false)
+ Assert.fail(message.get())
+ }
+
+ /** Interface representing a generic condition */
+ fun interface Condition {
+
+ @Throws(Throwable::class) fun isTrue(): Boolean
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 7bdc040..3b85309 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -12,7 +12,7 @@
import com.android.app.viewcapture.data.ExportedData;
import com.android.launcher3.tapl.LauncherInstrumentation;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.BaseLauncherTaplTest;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
@@ -57,7 +57,7 @@
@Override
protected void succeeded(Description description) {
super.succeeded(description);
- AbstractLauncherUiTest.checkDetectedLeaks(mLauncher);
+ BaseLauncherTaplTest.checkDetectedLeaks(mLauncher);
}
@Override
diff --git a/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java
index 7ff55fe..7cb2614 100644
--- a/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java
+++ b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java
@@ -163,6 +163,32 @@
}
/**
+ * Dismisses the Keyboard Quick Switch view by going home. After the Keyboard Quick Switch view
+ * gets hidden, it unpresses ALT key, which is generally used to keep the view visible.
+ */
+ public Workspace dismissByGoingHome() {
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "verifying keyboard quick switch view is shown")) {
+ mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
+ }
+
+ mLauncher.goHome();
+
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "waiting for keyboard quick switch dismissal");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ mLauncher.waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
+ }
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+ "get workspace after releasing ALT key")) {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_HOME_ALT_LEFT_UP);
+ mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0);
+ return mLauncher.getWorkspace();
+ }
+ }
+
+ /**
* Launches the currently-focused app task.
* <p>
* This method should only be used if the focused task is for a recent running app, otherwise
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 08c5552..fac73d3 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -1728,6 +1728,27 @@
scrollDownByDistance(container, distance, appsListBottomPadding);
}
+ /** Scrolls up by given distance within the container. */
+ void scrollUpByDistance(UiObject2 container, int distance) {
+ scrollUpByDistance(container, distance, 0);
+ }
+
+ /** Scrolls up by given distance within the container considering the given bottom padding. */
+ void scrollUpByDistance(UiObject2 container, int distance, int bottomPadding) {
+ final Rect containerRect = getVisibleBounds(container);
+ final int bottomGestureMarginInContainer = getBottomGestureMarginInContainer(container);
+ scroll(
+ container,
+ Direction.UP,
+ new Rect(
+ 0,
+ containerRect.height() - bottomGestureMarginInContainer - distance,
+ 0,
+ bottomGestureMarginInContainer + bottomPadding),
+ /* steps= */ 10,
+ /* slowDown= */ true);
+ }
+
void scrollDownByDistance(UiObject2 container, int distance) {
scrollDownByDistance(container, distance, 0);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 3097d9c..ac2748e 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
import static com.android.launcher3.tapl.LauncherInstrumentation.log;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
@@ -31,6 +32,7 @@
import com.android.launcher3.testing.shared.TestProtocol;
import java.util.Collection;
+import java.util.List;
/**
* All widgets container.
@@ -128,8 +130,10 @@
final UiObject2 searchBar = findSearchBar();
final int searchBarHeight = searchBar.getVisibleBounds().height();
final UiObject2 fullWidgetsPicker = verifyActiveContainer();
- mLauncher.assertTrue("Widgets container didn't become scrollable",
- fullWidgetsPicker.wait(Until.scrollable(true), WAIT_TIME_MS));
+
+ // Widget picker may not be scrollable if there are few items. Instead of waiting on
+ // picker being scrollable, we wait on widget headers to be available.
+ waitForWidgetListItems(fullWidgetsPicker);
final UiObject2 widgetsContainer =
findTestAppWidgetsTableContainer(testAppWidgetPackage);
@@ -176,6 +180,13 @@
}
}
+ private void waitForWidgetListItems(UiObject2 fullWidgetsPicker) {
+ List<UiObject2> headers = fullWidgetsPicker.wait(Until.findObjects(
+ By.res(mLauncher.getLauncherPackageName(), "widgets_list_header")), WAIT_TIME_MS);
+ mLauncher.assertTrue("Widgets list is not available",
+ headers != null && !headers.isEmpty());
+ }
+
private UiObject2 findSearchBar() {
final BySelector searchBarContainerSelector = By.res(mLauncher.getLauncherPackageName(),
"search_and_recommendations_container");
@@ -199,19 +210,38 @@
"container");
String packageName = mLauncher.getContext().getPackageName();
+ String packageNameToFind =
+ (testAppWidgetPackage == null || testAppWidgetPackage.isEmpty()) ? packageName
+ : testAppWidgetPackage;
+
final BySelector targetAppSelector = By
.clazz("android.widget.TextView")
- .text((testAppWidgetPackage == null || testAppWidgetPackage.isEmpty())
- ? packageName
- : testAppWidgetPackage);
+ .text(packageNameToFind);
+ final BySelector expandListButtonSelector =
+ By.res(mLauncher.getLauncherPackageName(), "widget_list_expand_button");
final BySelector widgetsContainerSelector = By.res(mLauncher.getLauncherPackageName(),
"widgets_table");
boolean hasHeaderExpanded = false;
+ // List was expanded by clicking "Show all" button.
+ boolean hasListExpanded = false;
+
int scrollDistance = 0;
for (int i = 0; i < SCROLL_ATTEMPTS; i++) {
UiObject2 widgetPicker = mLauncher.waitForLauncherObject(widgetPickerSelector);
UiObject2 widgetListView = verifyActiveContainer();
+
+ // Press "Show all" button if it exists. Otherwise, keep scrolling to
+ // find the header or show all button.
+ UiObject2 expandListButton =
+ mLauncher.findObjectInContainer(widgetListView, expandListButtonSelector);
+ if (expandListButton != null) {
+ expandListButton.click();
+ hasListExpanded = true;
+ i = -1;
+ continue;
+ }
+
UiObject2 header = mLauncher.waitForObjectInContainer(widgetListView,
headerSelector);
// If a header is barely visible in the bottom edge of the screen, its height could be
@@ -222,6 +252,17 @@
// Look for a header that has the test app name.
UiObject2 headerTitle = mLauncher.findObjectInContainer(widgetListView,
targetAppSelector);
+
+ final UiObject2 searchBar = findSearchBar();
+ // If header's title is under or above search bar, let's not process the header yet,
+ // scroll a bit more to bring the header into visible area.
+ if (headerTitle != null
+ && headerTitle.getVisibleCenter().y <= searchBar.getVisibleCenter().y) {
+ log("Test app's header is behind the searchbar, scrolling up");
+ mLauncher.scrollUpByDistance(widgetListView, scrollDistance);
+ continue;
+ }
+
if (headerTitle != null) {
// If we find the header and it has not been expanded, let's click it to see the
// widgets list. Note that we wait until the header is out of the gesture region at
@@ -258,11 +299,24 @@
widgetPicker,
widgetsContainerSelector);
- mLauncher.scrollDownByDistance(hasHeaderExpanded && rightPane != null
- ? rightPane
- : widgetListView, scrollDistance);
+ if (hasListExpanded && packageNameToFind.compareToIgnoreCase(
+ getFirstHeaderTitle(widgetListView)) < 0) {
+ mLauncher.scrollUpByDistance(hasHeaderExpanded && rightPane != null
+ ? rightPane
+ : widgetListView, scrollDistance);
+ } else {
+ mLauncher.scrollDownByDistance(hasHeaderExpanded && rightPane != null
+ ? rightPane
+ : widgetListView, scrollDistance);
+ }
}
return null;
}
+
+ @NonNull
+ private String getFirstHeaderTitle(UiObject2 widgetListView) {
+ UiObject2 firstHeader = mLauncher.getObjectsInContainer(widgetListView, "app_title").get(0);
+ return firstHeader != null ? firstHeader.getText() : "";
+ }
}