Merge branch 'ub-launcher3-master', build 5258802
Test: Manual
Bug:111697218 [Quickstep 1.1] Integrate PiP / Live Tiles into Quickstep
Bug:111698021 [Quickstep 1.1] Improve motion within Quickstep
Bug:111699315 [Quickstep 2] Improve Quickscrub / switch gesture
Bug:111926330 [Quickstep 2] Nav bar, gestures, edge-to-edge, and OEM updates
Bug:112324288 Pixel launcher keeps on crashing when trying to remove a widget
Bug:114136250 Have a more spartan RecentsActivity on android go
Bug:117332845 testPendingWidget_autoRestored is failing on TAP
Bug:118206886 Add fallback launcher mechanism
Bug:118319143 1Digital Wellbeing - Implement showing app's remaining time in Launcher
Bug:119500058 Tests switch Launcher to a state without icons
Bug:120628042 Add race condition repro tests
Bug:122262084 Expose Launcher grid customization options
Bug:122262946 Each app installed from playstore occupies new workspace
Bug:122345781 Inflation happens and a binder call during swipe up gesture, that may cause jank
Bug:122347746 Jank in app closing animation for Maps on taimen
Bug:122472687 Recents icon shape should change when theme changed.
Bug:122545624 Iconloaderlib: make createIconBitmap in BaseIconFactory a public method
Bug:122612839 Flash of old snapshot when swiping up
Bug:122662526 Test broken: can't swipe back from AllApps to Overview
Bug:122809947 [C2][F2] Launcher keeps restarting after Setup wizard, it impacts testing seriously.
Bug:65162781 Status bar gradient sometimes slides up when unlocking
Change-Id: I22774df74963efb9189ea2a8bf3cdace6361c988
diff --git a/Android.mk b/Android.mk
index b330a54..a5e338f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -182,15 +182,20 @@
endif
LOCAL_MODULE := Launcher3QuickStepLib
LOCAL_PRIVILEGED_MODULE := true
-LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ Launcher3CommonDepsLib \
+ SecondaryDisplayLauncherLib
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
$(call all-java-files-under, quickstep/src) \
+ $(call all-java-files-under, quickstep/recents_ui_overrides/src) \
$(call all-java-files-under, src_flags) \
$(call all-java-files-under, src_shortcuts_overrides)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/quickstep/res
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/quickstep/res \
+ $(LOCAL_PATH)/quickstep/recents_ui_overrides/res
LOCAL_PROGUARD_ENABLED := disabled
@@ -219,7 +224,9 @@
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/quickstep/res
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/quickstep/res \
+ $(LOCAL_PATH)/quickstep/recents_ui_overrides/res
LOCAL_FULL_LIBS_MANIFEST_FILES := \
$(LOCAL_PATH)/AndroidManifest.xml \
@@ -251,10 +258,12 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
$(call all-java-files-under, quickstep/src) \
+ $(call all-java-files-under, quickstep/recents_ui_overrides/src) \
$(call all-java-files-under, go/src)
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/quickstep/res \
+ $(LOCAL_PATH)/quickstep/recents_ui_overrides/res \
$(LOCAL_PATH)/go/res
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
@@ -273,6 +282,51 @@
LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
include $(BUILD_PACKAGE)
+#
+# Build rule for Launcher3 Go app with quickstep and Go-specific
+# version of recents for Android Go devices.
+#
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE_TAGS := optional
+
+ifneq (,$(wildcard frameworks/base))
+ LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
+ LOCAL_PRIVATE_PLATFORM_APIS := true
+else
+ LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI libLauncherProtos
+ LOCAL_SDK_VERSION := system_current
+ LOCAL_MIN_SDK_VERSION := 26
+endif
+LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-java-files-under, quickstep/src) \
+ $(call all-java-files-under, go/src) \
+ $(call all-java-files-under, go/quickstep/src)
+
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/quickstep/res \
+ $(LOCAL_PATH)/go/res \
+ $(LOCAL_PATH)/go/quickstep/res
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+LOCAL_PROGUARD_ENABLED := full
+
+LOCAL_PACKAGE_NAME := Launcher3QuickStepGoIconRecents
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
+
+LOCAL_FULL_LIBS_MANIFEST_FILES := \
+ $(LOCAL_PATH)/go/AndroidManifest.xml \
+ $(LOCAL_PATH)/AndroidManifest.xml \
+ $(LOCAL_PATH)/AndroidManifest-common.xml
+
+LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
+LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
+include $(BUILD_PACKAGE)
+
# ==================================================
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index d7c16e6..979096c 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -153,6 +153,15 @@
android:readPermission="${packageName}.permission.READ_SETTINGS" />
<!--
+ The content provider for exposing various launcher grid options.
+ TODO: Add proper permissions
+ -->
+ <provider
+ android:name="com.android.launcher3.graphics.GridOptionsProvider"
+ android:authorities="${packageName}.grid_control"
+ android:exported="true" />
+
+ <!--
The settings activity. To extend point settings_fragment_name to appropriate fragment class
-->
<activity
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 1a485ed..9500a2f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -61,6 +61,9 @@
<category android:name="android.intent.category.MONKEY"/>
<category android:name="android.intent.category.LAUNCHER_APP" />
</intent-filter>
+ <meta-data
+ android:name="com.android.launcher3.grid.control"
+ android:value="${packageName}.grid.control" />
</activity>
</application>
diff --git a/SecondaryDisplayLauncher/res/values-af/strings.xml b/SecondaryDisplayLauncher/res/values-af/strings.xml
index 63d83a2..b544be7a 100644
--- a/SecondaryDisplayLauncher/res/values-af/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-af/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Kon nie die aktiwiteit begin nie"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Voeg programkortpad by"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Stel muurpapier"</string>
diff --git a/SecondaryDisplayLauncher/res/values-am/strings.xml b/SecondaryDisplayLauncher/res/values-am/strings.xml
index 3d99783..71854ad 100644
--- a/SecondaryDisplayLauncher/res/values-am/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-am/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"እንቅስቃሴውን ማስጀመር አልተቻለም"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"የመተግበሪያ አቋራጭ ያክሉ"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"ልጣፍ አዘጋጅ"</string>
diff --git a/SecondaryDisplayLauncher/res/values-ar/strings.xml b/SecondaryDisplayLauncher/res/values-ar/strings.xml
index 1c0ee17..ba81c11 100644
--- a/SecondaryDisplayLauncher/res/values-ar/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ar/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"تعذَّر تشغيل النشاط."</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"إضافة اختصار التطبيق"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"تعيين الخلفية"</string>
diff --git a/SecondaryDisplayLauncher/res/values-as/strings.xml b/SecondaryDisplayLauncher/res/values-as/strings.xml
index 5e4f690..d199a26 100644
--- a/SecondaryDisplayLauncher/res/values-as/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-as/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"কাৰ্যকলাপটো লঞ্চ কৰিব পৰা নগ’ল"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"এপৰ শ্বর্টকাট যোগ কৰক"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"ৱালপেপাৰ ছেট কৰক"</string>
diff --git a/SecondaryDisplayLauncher/res/values-az/strings.xml b/SecondaryDisplayLauncher/res/values-az/strings.xml
index 5cc7313..cee70a0 100644
--- a/SecondaryDisplayLauncher/res/values-az/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-az/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"Əlavə Displey Başladıcısı"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Fəaliyyəti başlatmaq mümkün olmadı"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Tətbiq qısayolu əlavə edin"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Divar kağızı ayarlayın"</string>
diff --git a/SecondaryDisplayLauncher/res/values-b+sr+Latn/strings.xml b/SecondaryDisplayLauncher/res/values-b+sr+Latn/strings.xml
index 873b1ea..a8859d9 100644
--- a/SecondaryDisplayLauncher/res/values-b+sr+Latn/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-b+sr+Latn/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Pokretanje aktivnosti nije uspelo"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Dodaj prečicu za aplikaciju"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Podesite pozadinu"</string>
diff --git a/SecondaryDisplayLauncher/res/values-be/strings.xml b/SecondaryDisplayLauncher/res/values-be/strings.xml
index bb59e46..3df3760 100644
--- a/SecondaryDisplayLauncher/res/values-be/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-be/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Не ўдалося запусціць дзеянне"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Дадаць ярлык праграмы"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Устанавіць шпалеры"</string>
diff --git a/SecondaryDisplayLauncher/res/values-bg/strings.xml b/SecondaryDisplayLauncher/res/values-bg/strings.xml
index 9c2abbb..4474815 100644
--- a/SecondaryDisplayLauncher/res/values-bg/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-bg/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Активността не можа да бъде стартирана"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Добавяне на пряк път към приложението"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Задаване на тапет"</string>
diff --git a/SecondaryDisplayLauncher/res/values-bn/strings.xml b/SecondaryDisplayLauncher/res/values-bn/strings.xml
index 5500754..7322691 100644
--- a/SecondaryDisplayLauncher/res/values-bn/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-bn/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"অ্যাক্টিভিটি চালু করা যায়নি"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"অ্যাপ শর্টকাট যোগ করুন"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"ওয়ালপেপার সেট করুন"</string>
diff --git a/SecondaryDisplayLauncher/res/values-bs/strings.xml b/SecondaryDisplayLauncher/res/values-bs/strings.xml
index 7d2f011..1e59d33 100644
--- a/SecondaryDisplayLauncher/res/values-bs/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-bs/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"Pokretač za sekundarni ekran"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Pokretanje aktivnosti nije uspjelo"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Dodaj prečicu aplikacije"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Postavi pozadinsku sliku"</string>
diff --git a/SecondaryDisplayLauncher/res/values-ca/strings.xml b/SecondaryDisplayLauncher/res/values-ca/strings.xml
index f17c30c..c0274d1 100644
--- a/SecondaryDisplayLauncher/res/values-ca/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ca/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"No s\'ha pogut iniciar l\'activitat"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Afegeix una drecera d\'aplicació"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Estableix el fons de pantalla"</string>
diff --git a/SecondaryDisplayLauncher/res/values-cs/strings.xml b/SecondaryDisplayLauncher/res/values-cs/strings.xml
index 1864aac..92ed5fa 100644
--- a/SecondaryDisplayLauncher/res/values-cs/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-cs/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Aktivitu nelze zahájit"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Přidat zkratku aplikace"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Nastavení tapety"</string>
diff --git a/SecondaryDisplayLauncher/res/values-da/strings.xml b/SecondaryDisplayLauncher/res/values-da/strings.xml
index 0ad86e1..16bdb33 100644
--- a/SecondaryDisplayLauncher/res/values-da/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-da/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Aktiviteten kunne ikke startes"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Tilføj appgenvej"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Angiv baggrund"</string>
diff --git a/SecondaryDisplayLauncher/res/values-de/strings.xml b/SecondaryDisplayLauncher/res/values-de/strings.xml
index fe1b103..3617a5b 100644
--- a/SecondaryDisplayLauncher/res/values-de/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-de/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Aktivität konnte nicht gestartet werden"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"App-Verknüpfung hinzufügen"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Hintergrund festlegen"</string>
diff --git a/SecondaryDisplayLauncher/res/values-el/strings.xml b/SecondaryDisplayLauncher/res/values-el/strings.xml
index 1a62114..8d19d09 100644
--- a/SecondaryDisplayLauncher/res/values-el/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-el/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Δεν ήταν δυνατή η εκκίνηση της δραστηριότητας"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Προσθήκη συντόμευσης εφαρμογής"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Ορισμός ταπετσαρίας"</string>
diff --git a/SecondaryDisplayLauncher/res/values-en-rAU/strings.xml b/SecondaryDisplayLauncher/res/values-en-rAU/strings.xml
index ab55a6a..8d8c419 100644
--- a/SecondaryDisplayLauncher/res/values-en-rAU/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-en-rAU/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Couldn\'t launch the activity"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Add app shortcut"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Set wallpaper"</string>
diff --git a/SecondaryDisplayLauncher/res/values-en-rGB/strings.xml b/SecondaryDisplayLauncher/res/values-en-rGB/strings.xml
index ab55a6a..8d8c419 100644
--- a/SecondaryDisplayLauncher/res/values-en-rGB/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-en-rGB/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Couldn\'t launch the activity"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Add app shortcut"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Set wallpaper"</string>
diff --git a/SecondaryDisplayLauncher/res/values-en-rIN/strings.xml b/SecondaryDisplayLauncher/res/values-en-rIN/strings.xml
index ab55a6a..8d8c419 100644
--- a/SecondaryDisplayLauncher/res/values-en-rIN/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-en-rIN/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Couldn\'t launch the activity"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Add app shortcut"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Set wallpaper"</string>
diff --git a/SecondaryDisplayLauncher/res/values-es-rUS/strings.xml b/SecondaryDisplayLauncher/res/values-es-rUS/strings.xml
index 64b686c..ff6772b 100644
--- a/SecondaryDisplayLauncher/res/values-es-rUS/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-es-rUS/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"No se pudo iniciar la actividad"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Agregar acceso directo a app"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Establecer fondo de pantalla"</string>
diff --git a/SecondaryDisplayLauncher/res/values-es/strings.xml b/SecondaryDisplayLauncher/res/values-es/strings.xml
index fa5e4b4..0654dcb 100644
--- a/SecondaryDisplayLauncher/res/values-es/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-es/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"No se ha podido iniciar la acción"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Añadir acceso directo a la aplicación"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Establecer fondo de pantalla"</string>
diff --git a/SecondaryDisplayLauncher/res/values-et/strings.xml b/SecondaryDisplayLauncher/res/values-et/strings.xml
index 8fde9dd..3410fd4 100644
--- a/SecondaryDisplayLauncher/res/values-et/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-et/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Tegevust ei saanud käivitada"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Lisa rakenduse otsetee"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Määra taustapilt"</string>
diff --git a/SecondaryDisplayLauncher/res/values-eu/strings.xml b/SecondaryDisplayLauncher/res/values-eu/strings.xml
index 5048127..d7abe33 100644
--- a/SecondaryDisplayLauncher/res/values-eu/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-eu/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Ezin izan da abiarazi jarduera"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Gehitu aplikaziorako lasterbidea"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Ezarri horma-papera"</string>
diff --git a/SecondaryDisplayLauncher/res/values-fa/strings.xml b/SecondaryDisplayLauncher/res/values-fa/strings.xml
index 2dc3780..4d3ec4d 100644
--- a/SecondaryDisplayLauncher/res/values-fa/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-fa/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"راهانداز نمایشگر ثانویه"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"فعالیت راهاندازی نشد"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"افزودن میانبر برنامه"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"تنظیم کاغذدیواری"</string>
diff --git a/SecondaryDisplayLauncher/res/values-fi/strings.xml b/SecondaryDisplayLauncher/res/values-fi/strings.xml
index 5d6d48f..e56f67a 100644
--- a/SecondaryDisplayLauncher/res/values-fi/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-fi/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Käynnistäminen epäonnistui"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Lisää sovelluksen pikakuvake"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Aseta taustakuva"</string>
diff --git a/SecondaryDisplayLauncher/res/values-fr-rCA/strings.xml b/SecondaryDisplayLauncher/res/values-fr-rCA/strings.xml
index 9d88e1f..f5c9ba5 100644
--- a/SecondaryDisplayLauncher/res/values-fr-rCA/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-fr-rCA/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Impossible de lancer l\'activité"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Ajouter un raccourci vers l\'application"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Définir le fond d\'écran"</string>
diff --git a/SecondaryDisplayLauncher/res/values-fr/strings.xml b/SecondaryDisplayLauncher/res/values-fr/strings.xml
index 10254bf..daa186b 100644
--- a/SecondaryDisplayLauncher/res/values-fr/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-fr/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Impossible de lancer l\'activité"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Ajouter un raccourci vers l\'application"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Configurer le fond d\'écran"</string>
diff --git a/SecondaryDisplayLauncher/res/values-gl/strings.xml b/SecondaryDisplayLauncher/res/values-gl/strings.xml
index eb86e39..0bcf969 100644
--- a/SecondaryDisplayLauncher/res/values-gl/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-gl/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"Launcher da pantalla secundaria"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Non se puido iniciar a actividade"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Engadir atallo da aplicación"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Definir fondo de pantalla"</string>
diff --git a/SecondaryDisplayLauncher/res/values-gu/strings.xml b/SecondaryDisplayLauncher/res/values-gu/strings.xml
index f13422e..82b4444 100644
--- a/SecondaryDisplayLauncher/res/values-gu/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-gu/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"પ્રવૃત્તિ લૉન્ચ કરી શકાઈ નથી"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"ઍપ શૉર્ટકટ ઉમેરો"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"વૉલપેપર સેટ કરો"</string>
diff --git a/SecondaryDisplayLauncher/res/values-hi/strings.xml b/SecondaryDisplayLauncher/res/values-hi/strings.xml
index 8b7728d..8adb519 100644
--- a/SecondaryDisplayLauncher/res/values-hi/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-hi/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"गतिविधि लॉन्च नहीं हो सकी"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"ऐप्लिकेशन शॉर्टकट जोड़ें"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"वॉलपेपर सेट करें"</string>
diff --git a/SecondaryDisplayLauncher/res/values-hr/strings.xml b/SecondaryDisplayLauncher/res/values-hr/strings.xml
index d622928..87ac874 100644
--- a/SecondaryDisplayLauncher/res/values-hr/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-hr/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"Pokretač za sekundarni zaslon"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Pokretanje aktivnosti nije uspjelo"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Dodajte aplikacijski prečac"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Postavljanje pozadine"</string>
diff --git a/SecondaryDisplayLauncher/res/values-hu/strings.xml b/SecondaryDisplayLauncher/res/values-hu/strings.xml
index 868a3bb..a8870fc 100644
--- a/SecondaryDisplayLauncher/res/values-hu/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-hu/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Nem sikerült elindítani a tevékenységet"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Alkalmazás parancsikonjának hozzáadása"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Háttérkép beállítása"</string>
diff --git a/SecondaryDisplayLauncher/res/values-hy/strings.xml b/SecondaryDisplayLauncher/res/values-hy/strings.xml
index 1fdd1c4..a64233f 100644
--- a/SecondaryDisplayLauncher/res/values-hy/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-hy/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Չհաջողվեց գործարկել գործողությունը"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Ավելացնել հավելվածի դյուրանցումը"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Դարձնել պաստառ"</string>
diff --git a/SecondaryDisplayLauncher/res/values-in/strings.xml b/SecondaryDisplayLauncher/res/values-in/strings.xml
index 4260e5f..f51d238 100644
--- a/SecondaryDisplayLauncher/res/values-in/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-in/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Tidak dapat meluncurkan aktivitas"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Tambahkan pintasan app"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Setel wallpaper"</string>
diff --git a/SecondaryDisplayLauncher/res/values-is/strings.xml b/SecondaryDisplayLauncher/res/values-is/strings.xml
index 3435c60..e8b3e97 100644
--- a/SecondaryDisplayLauncher/res/values-is/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-is/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Ekki tókst að ræsa aðgerðina"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Bæta við flýtileið forrita"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Velja veggfóður"</string>
diff --git a/SecondaryDisplayLauncher/res/values-it/strings.xml b/SecondaryDisplayLauncher/res/values-it/strings.xml
index 4cf2f44..4941515 100644
--- a/SecondaryDisplayLauncher/res/values-it/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-it/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Impossibile avviare l\'attività"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Aggiungi scorciatoia app"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Imposta sfondo"</string>
diff --git a/SecondaryDisplayLauncher/res/values-iw/strings.xml b/SecondaryDisplayLauncher/res/values-iw/strings.xml
index f904c38..06b0c42 100644
--- a/SecondaryDisplayLauncher/res/values-iw/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-iw/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"לא ניתן היה להפעיל את הפעילות"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"הוספת קיצור דרך של אפליקציה"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"הגדרת טפט"</string>
diff --git a/SecondaryDisplayLauncher/res/values-ja/strings.xml b/SecondaryDisplayLauncher/res/values-ja/strings.xml
index 3e85178..3ed7b2b 100644
--- a/SecondaryDisplayLauncher/res/values-ja/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ja/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"アクティビティを開始できませんでした"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"アプリのショートカットを追加"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"壁紙を設定"</string>
diff --git a/SecondaryDisplayLauncher/res/values-ka/strings.xml b/SecondaryDisplayLauncher/res/values-ka/strings.xml
index 1f388e3..ac85f70 100644
--- a/SecondaryDisplayLauncher/res/values-ka/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ka/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"აქტივობის გაშვება ვერ მოხერხდა"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"აპის მალსახმობის დამატება"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"ფონის დაყენება"</string>
diff --git a/SecondaryDisplayLauncher/res/values-kk/strings.xml b/SecondaryDisplayLauncher/res/values-kk/strings.xml
index a679b09..f9ac455 100644
--- a/SecondaryDisplayLauncher/res/values-kk/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-kk/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Әрекет іске қосылмады"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Қолданба таңбашасын енгізу"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Тұсқағаз орнату"</string>
diff --git a/SecondaryDisplayLauncher/res/values-km/strings.xml b/SecondaryDisplayLauncher/res/values-km/strings.xml
index 2f1c75f..afc050f 100644
--- a/SecondaryDisplayLauncher/res/values-km/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-km/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"មិនអាចចាប់ផ្តើមសកម្មភាពទេ"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"បញ្ចូលផ្លូវកាត់កម្មវិធី"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"កំណត់ផ្ទាំងរូបភាព"</string>
diff --git a/SecondaryDisplayLauncher/res/values-kn/strings.xml b/SecondaryDisplayLauncher/res/values-kn/strings.xml
index aa0ccc1..09c327f 100644
--- a/SecondaryDisplayLauncher/res/values-kn/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-kn/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"ಚಟುವಟಿಕೆಯನ್ನು ಲಾಂಚ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"ಆ್ಯಪ್ ಶಾರ್ಟ್ಕಟ್ ಸೇರಿಸಿ"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"ವಾಲ್ಪೇಪರ್ ಹೊಂದಿಸಿ"</string>
diff --git a/SecondaryDisplayLauncher/res/values-ko/strings.xml b/SecondaryDisplayLauncher/res/values-ko/strings.xml
index 5b4b0c7..6a02ac0 100644
--- a/SecondaryDisplayLauncher/res/values-ko/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ko/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"활동을 실행할 수 없음"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"앱 바로가기 추가"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"배경화면 설정"</string>
diff --git a/SecondaryDisplayLauncher/res/values-ky/strings.xml b/SecondaryDisplayLauncher/res/values-ky/strings.xml
index b62d486..56185fa 100644
--- a/SecondaryDisplayLauncher/res/values-ky/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ky/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"Кошумча экранды жүргүзгүч"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Аракет аткарылган жок"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Колдонмого кыска жол кошуу"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Тушкагаз орнотуу"</string>
diff --git a/SecondaryDisplayLauncher/res/values-lo/strings.xml b/SecondaryDisplayLauncher/res/values-lo/strings.xml
index 5115547..36a6275 100644
--- a/SecondaryDisplayLauncher/res/values-lo/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-lo/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"ບໍ່ສາມາດເປີດໃຊ້ການເຄື່ອນໄຫວໄດ້"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"ເພີ່ມທາງລັດແອັບ"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"ຕັ້ງເປັນຮູບພື້ນຫຼັງ"</string>
diff --git a/SecondaryDisplayLauncher/res/values-lt/strings.xml b/SecondaryDisplayLauncher/res/values-lt/strings.xml
index 6ec68f9..8113eb6 100644
--- a/SecondaryDisplayLauncher/res/values-lt/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-lt/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Nepavyko paleisti veiklos"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Pridėti programos šaukinį"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Nustatyti ekrano foną"</string>
diff --git a/SecondaryDisplayLauncher/res/values-lv/strings.xml b/SecondaryDisplayLauncher/res/values-lv/strings.xml
index d708f13..e267933 100644
--- a/SecondaryDisplayLauncher/res/values-lv/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-lv/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Nevarēja palaist darbību"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Pievienot lietotnes saīsni"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Iestatīt fona tapeti"</string>
diff --git a/SecondaryDisplayLauncher/res/values-mk/strings.xml b/SecondaryDisplayLauncher/res/values-mk/strings.xml
index 5421ea8..e2cca03 100644
--- a/SecondaryDisplayLauncher/res/values-mk/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-mk/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"Стартер на секундарен екран"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Не можеше да се стартува активноста"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Додајте кратенка за апликација"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Поставете го тапетот"</string>
diff --git a/SecondaryDisplayLauncher/res/values-ml/strings.xml b/SecondaryDisplayLauncher/res/values-ml/strings.xml
index 308592b..864245b 100644
--- a/SecondaryDisplayLauncher/res/values-ml/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ml/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"ആക്റ്റിവിറ്റി പ്രകാശിപ്പിക്കാനായില്ല"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"ആപ്പ് കുറുക്കുവഴികൾ ചേർക്കുക"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"വാൾപേപ്പർ സജ്ജീകരിക്കുക"</string>
diff --git a/SecondaryDisplayLauncher/res/values-mn/strings.xml b/SecondaryDisplayLauncher/res/values-mn/strings.xml
index f0ae3fd..85fb020 100644
--- a/SecondaryDisplayLauncher/res/values-mn/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-mn/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Үйл ажиллагааг эхлүүж чадсангүй"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Аппын товчлол нэмэх"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Ханын зураг тохируулах"</string>
diff --git a/SecondaryDisplayLauncher/res/values-mr/strings.xml b/SecondaryDisplayLauncher/res/values-mr/strings.xml
index d54cc89..6e92a2f 100644
--- a/SecondaryDisplayLauncher/res/values-mr/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-mr/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"अॅक्टिव्हिटी लाँच करता आली नाही"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"अॅप शॉर्टकट जोडा"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"वॉलपेपर सेट करा"</string>
diff --git a/SecondaryDisplayLauncher/res/values-ms/strings.xml b/SecondaryDisplayLauncher/res/values-ms/strings.xml
index 16dfee0..fd78053 100644
--- a/SecondaryDisplayLauncher/res/values-ms/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ms/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Tidak dapat melancarkan aktiviti"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Tambah pintasan apl"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Tetapkan kertas dinding"</string>
diff --git a/SecondaryDisplayLauncher/res/values-my/strings.xml b/SecondaryDisplayLauncher/res/values-my/strings.xml
index a252c52..1521402 100644
--- a/SecondaryDisplayLauncher/res/values-my/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-my/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"အရန် ဖော်ပြမှု စတင်ခြင်းစနစ်"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"လုပ်ဆောင်ချက်ကို စတင်၍မရပါ"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"အက်ပ်ဖြတ်လမ်းလင့်ခ်ထည့်ရန်"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"နောက်ခံ သတ်မှတ်ရန်"</string>
diff --git a/SecondaryDisplayLauncher/res/values-nb/strings.xml b/SecondaryDisplayLauncher/res/values-nb/strings.xml
index b46835a..945c87b 100644
--- a/SecondaryDisplayLauncher/res/values-nb/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-nb/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Kunne ikke starte aktiviteten"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Legg til en appsnarvei"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Angi bakgrunn"</string>
diff --git a/SecondaryDisplayLauncher/res/values-ne/strings.xml b/SecondaryDisplayLauncher/res/values-ne/strings.xml
index d40d806..9a5b0a0 100644
--- a/SecondaryDisplayLauncher/res/values-ne/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ne/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"उक्त क्रियाकलाप सुरु गर्न सकिएन"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"अनुप्रयोगको सर्टकट थप्नुहोस्"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"वालपेपर सेट गर्नुहोस्"</string>
diff --git a/SecondaryDisplayLauncher/res/values-nl/strings.xml b/SecondaryDisplayLauncher/res/values-nl/strings.xml
index ad10233..8767708 100644
--- a/SecondaryDisplayLauncher/res/values-nl/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-nl/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Kan de activiteit niet starten"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"App-snelkoppeling toevoegen"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Achtergrond instellen"</string>
diff --git a/SecondaryDisplayLauncher/res/values-or/strings.xml b/SecondaryDisplayLauncher/res/values-or/strings.xml
index 6f04f99..9bc5725 100644
--- a/SecondaryDisplayLauncher/res/values-or/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-or/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"ଗତିବିଧିକୁ ଲଞ୍ଚ କରାଯାଇପାରିଲା ନାହିଁ"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"ଆପ୍ ସର୍ଟକଟ୍ ଯୋଗ କରନ୍ତୁ"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"ୱାଲ୍ପେପର୍କୁ ସେଟ୍ କରନ୍ତୁ"</string>
diff --git a/SecondaryDisplayLauncher/res/values-pa/strings.xml b/SecondaryDisplayLauncher/res/values-pa/strings.xml
index 8657ef5..c5dd582 100644
--- a/SecondaryDisplayLauncher/res/values-pa/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-pa/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"ਸਰਗਰਮੀ ਨੂੰ ਲਾਂਚ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"ਐਪ ਸ਼ਾਰਟਕੱਟ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"ਵਾਲਪੇਪਰ ਸੈੱਟ ਕਰੋ"</string>
diff --git a/SecondaryDisplayLauncher/res/values-pl/strings.xml b/SecondaryDisplayLauncher/res/values-pl/strings.xml
index 1eec235..e8efaed 100644
--- a/SecondaryDisplayLauncher/res/values-pl/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-pl/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Nie udało się uruchomić aktywności"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Dodaj skrót do aplikacji"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Ustaw tapetę"</string>
diff --git a/SecondaryDisplayLauncher/res/values-pt-rPT/strings.xml b/SecondaryDisplayLauncher/res/values-pt-rPT/strings.xml
index 7fadf3c..67c7557 100644
--- a/SecondaryDisplayLauncher/res/values-pt-rPT/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-pt-rPT/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Não foi possível iniciar a atividade."</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Adicionar atalho de aplicação"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Definir imagem de fundo"</string>
diff --git a/SecondaryDisplayLauncher/res/values-pt/strings.xml b/SecondaryDisplayLauncher/res/values-pt/strings.xml
index e8f6a55..201fc07 100644
--- a/SecondaryDisplayLauncher/res/values-pt/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-pt/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Não foi possível abrir a atividade"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Adicionar atalho de apps"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Definir plano de fundo"</string>
diff --git a/SecondaryDisplayLauncher/res/values-ro/strings.xml b/SecondaryDisplayLauncher/res/values-ro/strings.xml
index d485b2b..e2e21c5 100644
--- a/SecondaryDisplayLauncher/res/values-ro/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ro/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Nu s-a putut lansa activitatea"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Adăugați comanda rapidă pentru aplicație"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Setați imaginea de fundal"</string>
diff --git a/SecondaryDisplayLauncher/res/values-ru/strings.xml b/SecondaryDisplayLauncher/res/values-ru/strings.xml
index 5c4df1d..64ba00e 100644
--- a/SecondaryDisplayLauncher/res/values-ru/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ru/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Не удалось запустить объект activity"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Добавить ярлык приложения"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Установить обои"</string>
diff --git a/SecondaryDisplayLauncher/res/values-si/strings.xml b/SecondaryDisplayLauncher/res/values-si/strings.xml
index f665f9e..ac492eb 100644
--- a/SecondaryDisplayLauncher/res/values-si/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-si/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"ක්රියාකාරකම දියත් කිරීමට නොහැකි විය"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"යෙදුම් කෙටිමඟ එක් කරන්න"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"බිතුපත සකසන්න"</string>
diff --git a/SecondaryDisplayLauncher/res/values-sk/strings.xml b/SecondaryDisplayLauncher/res/values-sk/strings.xml
index 5e8d67a..5e6fa7a 100644
--- a/SecondaryDisplayLauncher/res/values-sk/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-sk/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Aktivitu sa nepodarilo spustiť"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Pridať odkaz do aplikácie"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Nastaviť tapetu"</string>
diff --git a/SecondaryDisplayLauncher/res/values-sl/strings.xml b/SecondaryDisplayLauncher/res/values-sl/strings.xml
index 864a9bb..f54dec9 100644
--- a/SecondaryDisplayLauncher/res/values-sl/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-sl/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"Zaganjalnik sekundarnega zaslona"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Dejavnosti ni bilo mogoče zagnati"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Dodaj bližnjico do aplikacije"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Nastavi ozadje"</string>
diff --git a/SecondaryDisplayLauncher/res/values-sq/strings.xml b/SecondaryDisplayLauncher/res/values-sq/strings.xml
index 486f82a..e626dd1 100644
--- a/SecondaryDisplayLauncher/res/values-sq/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-sq/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"Nisësi i ekranit dytësor"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Aktiviteti nuk mund të hapej"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Shto shkurtoren e aplikacionit"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Cakto imazhin e sfondit"</string>
diff --git a/SecondaryDisplayLauncher/res/values-sr/strings.xml b/SecondaryDisplayLauncher/res/values-sr/strings.xml
index e85b741..94214f1 100644
--- a/SecondaryDisplayLauncher/res/values-sr/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-sr/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Покретање активности није успело"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Додај пречицу за апликацију"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Подесите позадину"</string>
diff --git a/SecondaryDisplayLauncher/res/values-sv/strings.xml b/SecondaryDisplayLauncher/res/values-sv/strings.xml
index ec858db..53e17ef 100644
--- a/SecondaryDisplayLauncher/res/values-sv/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-sv/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Det gick inte att starta aktiviteten"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Lägg till appgenväg"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Ange bakgrund"</string>
diff --git a/SecondaryDisplayLauncher/res/values-sw/strings.xml b/SecondaryDisplayLauncher/res/values-sw/strings.xml
index 27e404d..490561a 100644
--- a/SecondaryDisplayLauncher/res/values-sw/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-sw/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"Mwonekano Mbadala wa Kifungua Programu"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Imeshindwa kuanzisha shughuli"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Ongeza njia ya mkato ya programu"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Weka mandhari"</string>
diff --git a/SecondaryDisplayLauncher/res/values-ta/strings.xml b/SecondaryDisplayLauncher/res/values-ta/strings.xml
index e2cfb4b..2f1262f 100644
--- a/SecondaryDisplayLauncher/res/values-ta/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ta/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"நடவடிக்கையைத் துவக்க இயலவில்லை"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"ஆப்ஸ் குறுக்குவழியைச் சேர்"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"வால்பேப்பரை அமை"</string>
diff --git a/SecondaryDisplayLauncher/res/values-te/strings.xml b/SecondaryDisplayLauncher/res/values-te/strings.xml
index f204e2a..3dd3c9b 100644
--- a/SecondaryDisplayLauncher/res/values-te/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-te/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"కార్యకలాపాన్ని ప్రారంభించడం సాధ్యం కాలేదు"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"యాప్ షార్ట్కట్ని జోడించు"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"వాల్పేపర్ను సెట్ చేయండి"</string>
diff --git a/SecondaryDisplayLauncher/res/values-th/strings.xml b/SecondaryDisplayLauncher/res/values-th/strings.xml
index ab4ca1b..6368950 100644
--- a/SecondaryDisplayLauncher/res/values-th/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-th/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"เปิดกิจกรรมไม่ได้"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"เพิ่มทางลัดของแอป"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"ตั้งวอลเปเปอร์"</string>
diff --git a/SecondaryDisplayLauncher/res/values-tl/strings.xml b/SecondaryDisplayLauncher/res/values-tl/strings.xml
index 2189cf9..192e5c4 100644
--- a/SecondaryDisplayLauncher/res/values-tl/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-tl/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Hindi mailunsad ang aktibidad"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Magdagdag ng shortcut ng app"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Magtakda ng wallpaper"</string>
diff --git a/SecondaryDisplayLauncher/res/values-tr/strings.xml b/SecondaryDisplayLauncher/res/values-tr/strings.xml
index 5a47662..e7ed998 100644
--- a/SecondaryDisplayLauncher/res/values-tr/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-tr/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"İşlem başlatılamadı"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Uygulama kısayolu ekle"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Duvar kağıdı ayarla"</string>
diff --git a/SecondaryDisplayLauncher/res/values-uk/strings.xml b/SecondaryDisplayLauncher/res/values-uk/strings.xml
index 6301b1d..e465995 100644
--- a/SecondaryDisplayLauncher/res/values-uk/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-uk/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"Панель запуску додаткового екрана"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Не вдалося запустити активність"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Розмістити ярлик додатка"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Вибрати фоновий малюнок"</string>
diff --git a/SecondaryDisplayLauncher/res/values-ur/strings.xml b/SecondaryDisplayLauncher/res/values-ur/strings.xml
index 35ae0dc..e4c8641 100644
--- a/SecondaryDisplayLauncher/res/values-ur/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ur/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"سرگرمی کو شروع نہیں کیا جا سکا"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"ایپ شارٹ کٹ شامل کریں"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"وال پیپر سیٹ کریں"</string>
diff --git a/SecondaryDisplayLauncher/res/values-uz/strings.xml b/SecondaryDisplayLauncher/res/values-uz/strings.xml
index 5708a1a..585739d 100644
--- a/SecondaryDisplayLauncher/res/values-uz/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-uz/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Faollik ishga tushmadi"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Yorliq yaratish"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Fonga rasm oʻrnatish"</string>
diff --git a/SecondaryDisplayLauncher/res/values-vi/strings.xml b/SecondaryDisplayLauncher/res/values-vi/strings.xml
index 4cd3943..15a1a44 100644
--- a/SecondaryDisplayLauncher/res/values-vi/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-vi/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Không thể chạy hoạt động"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Thêm lối tắt ứng dụng"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Đặt hình nền"</string>
diff --git a/SecondaryDisplayLauncher/res/values-zh-rCN/strings.xml b/SecondaryDisplayLauncher/res/values-zh-rCN/strings.xml
index 2318f92..3358499 100644
--- a/SecondaryDisplayLauncher/res/values-zh-rCN/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-zh-rCN/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"辅助显示屏启动器"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"无法启动该操作组件"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"添加应用快捷方式"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"设置壁纸"</string>
diff --git a/SecondaryDisplayLauncher/res/values-zh-rHK/strings.xml b/SecondaryDisplayLauncher/res/values-zh-rHK/strings.xml
index 3ac8ac2..bf76f29 100644
--- a/SecondaryDisplayLauncher/res/values-zh-rHK/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-zh-rHK/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"次要顯示屏啟動器"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"無法啟動活動"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"新增應用程式捷徑"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"設定桌布"</string>
diff --git a/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml b/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml
index a0e4180..bf76f29 100644
--- a/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"無法啟動活動"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"新增應用程式捷徑"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"設定桌布"</string>
diff --git a/SecondaryDisplayLauncher/res/values-zu/strings.xml b/SecondaryDisplayLauncher/res/values-zu/strings.xml
index 9dc423c..ad2f6b9 100644
--- a/SecondaryDisplayLauncher/res/values-zu/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-zu/strings.xml
@@ -19,7 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="8116646702313548897">"SecondaryDisplayLauncher"</string>
<string name="couldnt_launch" msgid="7873588052226763866">"Ayikwazanga ukuqalisa umsebenzi"</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"Engeza isinqamuleli sohlelo lokusebenza"</string>
<string name="set_wallpaper" msgid="6475195450505435904">"Setha isithombe sangemuva"</string>
diff --git a/build.gradle b/build.gradle
index 33409c5..4191d47 100644
--- a/build.gradle
+++ b/build.gradle
@@ -36,41 +36,56 @@
targetCompatibility JavaVersion.VERSION_1_8
}
- flavorDimensions "default"
+ // The flavor dimensions for build variants (e.g. aospWithQuickstep, aospWithoutQuickstep)
+ // See: https://developer.android.com/studio/build/build-variants#flavor-dimensions
+ flavorDimensions "app", "recents"
productFlavors {
aosp {
- dimension "default"
+ dimension "app"
applicationId 'com.android.launcher3'
testApplicationId 'com.android.launcher3.tests'
}
l3go {
- dimension "default"
+ dimension "app"
applicationId 'com.android.launcher3'
testApplicationId 'com.android.launcher3.tests'
}
- quickstep {
- dimension "default"
- applicationId 'com.android.launcher3'
- testApplicationId 'com.android.launcher3.tests'
+ withQuickstep {
+ dimension "recents"
minSdkVersion 28
}
+
+ withQuickstepIconRecents {
+ dimension "recents"
+
+ minSdkVersion 28
+ }
+
+ withoutQuickstep {
+ dimension "recents"
+ }
}
// Disable release builds for now
android.variantFilter { variant ->
if (variant.buildType.name.endsWith('release')) {
- variant.setIgnore(true);
+ variant.setIgnore(true)
+ }
+
+ // Icon recents is Go only
+ if (name.contains("WithQuickstepIconRecents") && !name.contains("l3go")) {
+ variant.setIgnore(true)
}
}
sourceSets {
main {
res.srcDirs = ['res']
- java.srcDirs = ['src', 'src_shortcuts_overrides']
+ java.srcDirs = ['src', 'src_plugins']
manifest.srcFile 'AndroidManifest-common.xml'
proto {
srcDir 'protos/'
@@ -93,18 +108,29 @@
}
aosp {
- java.srcDirs = ['src_flags', "src_ui_overrides"]
+ java.srcDirs = ['src_flags', 'src_shortcuts_overrides']
+ manifest.srcFile "AndroidManifest.xml"
}
l3go {
res.srcDirs = ['go/res']
- java.srcDirs = ['go/src', "src_ui_overrides"]
+ java.srcDirs = ['go/src']
manifest.srcFile "go/AndroidManifest.xml"
}
- quickstep {
- res.srcDirs = ['quickstep/res']
- java.srcDirs = ['src_flags', 'quickstep/src']
+ withoutQuickstep {
+ java.srcDirs = ['src_ui_overrides']
+ }
+
+ withQuickstep {
+ res.srcDirs = ['quickstep/res', 'quickstep/recents_ui_overrides/res']
+ java.srcDirs = ['quickstep/src', 'quickstep/recents_ui_overrides/src']
+ manifest.srcFile "quickstep/AndroidManifest.xml"
+ }
+
+ withQuickstepIconRecents {
+ res.srcDirs = ['quickstep/res', 'go/quickstep/res']
+ java.srcDirs = ['quickstep/src', 'go/quickstep/src']
manifest.srcFile "quickstep/AndroidManifest.xml"
}
}
@@ -121,14 +147,17 @@
implementation "androidx.dynamicanimation:dynamicanimation:${ANDROID_X_VERSION}"
implementation "androidx.recyclerview:recyclerview:${ANDROID_X_VERSION}"
implementation "androidx.preference:preference:${ANDROID_X_VERSION}"
- implementation PROTOBUF_DEPENDENCY
implementation project(':IconLoader')
+ implementation fileTree(dir: "libs", include: 'launcher_protos.jar')
- // This is already included in sysui_shared
- aospImplementation fileTree(dir: "libs", include: 'plugin_core.jar')
- l3goImplementation fileTree(dir: "libs", include: 'plugin_core.jar')
+ // Recents lib dependency
+ withQuickstepImplementation fileTree(dir: "quickstep/libs", include: 'sysui_shared.jar')
- quickstepImplementation fileTree(dir: "quickstep/libs", include: 'sysui_shared.jar')
+ // Recents lib dependency for Go
+ withQuickstepIconRecentsImplementation fileTree(dir: "quickstep/libs", include: 'sysui_shared.jar')
+
+ // Required for AOSP to compile. This is already included in the sysui_shared.jar
+ withoutQuickstepImplementation fileTree(dir: "libs", include: 'plugin_core.jar')
testImplementation 'junit:junit:4.12'
androidTestImplementation "org.mockito:mockito-core:1.9.5"
diff --git a/go/quickstep/res/layout/icon_recents_root_view.xml b/go/quickstep/res/layout/icon_recents_root_view.xml
new file mode 100644
index 0000000..82d5890
--- /dev/null
+++ b/go/quickstep/res/layout/icon_recents_root_view.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.quickstep.views.IconRecentsView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center">
+ <!-- TODO(114136250): Remove this temporary placeholder view for Go recents -->
+ <TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="Stub!"
+ android:textSize="40sp"/>
+</com.android.quickstep.views.IconRecentsView>
\ No newline at end of file
diff --git a/go/quickstep/res/layout/overview_panel.xml b/go/quickstep/res/layout/overview_panel.xml
new file mode 100644
index 0000000..601edce
--- /dev/null
+++ b/go/quickstep/res/layout/overview_panel.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<fragment android:name="com.android.quickstep.IconRecentsFragment"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/low_ram_recents_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
diff --git a/quickstep/res/values/override.xml b/go/quickstep/res/values/override.xml
similarity index 88%
copy from quickstep/res/values/override.xml
copy to go/quickstep/res/values/override.xml
index d683659..7636fb3 100644
--- a/quickstep/res/values/override.xml
+++ b/go/quickstep/res/values/override.xml
@@ -14,8 +14,10 @@
limitations under the License.
-->
+<!-- Class overrides for Go version of launcher with Go recents. -->
+
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_transition_manager_class" translatable="false">com.android.launcher3.LauncherAppTransitionManagerImpl</string>
+ <string name="app_transition_manager_class" translatable="false">com.android.launcher3.GoLauncherAppTransitionManagerImpl</string>
<string name="instant_app_resolver_class" translatable="false">com.android.quickstep.InstantAppResolverImpl</string>
diff --git a/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
new file mode 100644
index 0000000..95d8c4e
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
@@ -0,0 +1,65 @@
+package com.android.launcher3;
+
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.views.IconRecentsView.CONTENT_ALPHA;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.view.View;
+
+import com.android.quickstep.views.IconRecentsView;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * A {@link QuickstepAppTransitionManagerImpl} with recents-specific app transitions based off
+ * {@link com.android.quickstep.views.IconRecentsView}.
+ */
+public final class GoLauncherAppTransitionManagerImpl extends QuickstepAppTransitionManagerImpl {
+
+ public GoLauncherAppTransitionManagerImpl(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected boolean isLaunchingFromRecents(View v, RemoteAnimationTargetCompat[] targets) {
+ return mLauncher.getStateManager().getState().overviewUi;
+ }
+
+ @Override
+ protected boolean isQuickSwitchInProgress() {
+ // Go does not support quick scrub.
+ return false;
+ }
+
+ @Override
+ protected ActivityOptions getQuickSwitchActivityOptions() {
+ // Go does not support quick scrub.
+ return null;
+ }
+
+ @Override
+ protected void composeRecentsLaunchAnimator(AnimatorSet anim, View v,
+ RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
+ //TODO: Implement this based off IconRecentsView
+ }
+
+ @Override
+ protected Runnable composeViewContentAnimator(AnimatorSet anim, float[] alphas, float[] trans) {
+ IconRecentsView overview = mLauncher.getOverviewPanel();
+ ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
+ CONTENT_ALPHA, alphas);
+ alpha.setDuration(CONTENT_ALPHA_DURATION);
+ alpha.setInterpolator(LINEAR);
+ anim.play(alpha);
+
+ ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
+ transY.setInterpolator(AGGRESSIVE_EASE);
+ transY.setDuration(CONTENT_TRANSLATION_DURATION);
+ anim.play(transY);
+
+ return mLauncher.getStateManager()::reapplyState;
+ }
+}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
new file mode 100644
index 0000000..283e349
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+
+import android.graphics.Rect;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.quickstep.RecentsModel;
+
+/**
+ * Definition for overview state
+ */
+public class OverviewState extends LauncherState {
+
+ // TODO: Remove this when we refactor BackgroundAppState
+ protected static final Rect sTempRect = new Rect();
+
+ private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
+ | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
+
+ public OverviewState(int id) {
+ this(id, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
+ }
+
+ protected OverviewState(int id, int transitionDuration, int stateFlags) {
+ super(id, LauncherLogProto.ContainerType.TASKSWITCHER, transitionDuration, stateFlags);
+ }
+
+ @Override
+ public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
+ return new float[] {1f, 0f};
+ }
+
+ @Override
+ public void onStateDisabled(Launcher launcher) {
+ RecentsModel.INSTANCE.get(launcher).resetAssistCache();
+ }
+
+ @Override
+ public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
+ return new PageAlphaProvider(DEACCEL_2) {
+ @Override
+ public float getPageAlpha(int pageIndex) {
+ return 0;
+ }
+ };
+ }
+
+ @Override
+ public int getVisibleElements(Launcher launcher) {
+ return NONE;
+ }
+
+ @Override
+ public float getWorkspaceScrimAlpha(Launcher launcher) {
+ return 0.5f;
+ }
+
+ @Override
+ public String getDescription(Launcher launcher) {
+ return launcher.getString(R.string.accessibility_desc_recent_apps);
+ }
+
+ @Override
+ public void onBackPressed(Launcher launcher) {
+ // TODO: Add logic to go back to task if coming from a currently running task.
+ super.onBackPressed(launcher);
+ }
+
+
+ public static float getDefaultSwipeHeight(Launcher launcher) {
+ DeviceProfile dp = launcher.getDeviceProfile();
+ return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
+ }
+}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/PortraitOverviewStateTouchHelper.java b/go/quickstep/src/com/android/launcher3/uioverrides/PortraitOverviewStateTouchHelper.java
new file mode 100644
index 0000000..a3b41b0
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/PortraitOverviewStateTouchHelper.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import android.view.MotionEvent;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.util.PendingAnimation;
+
+/**
+ * Helper class for {@link PortraitStatesTouchController} that determines swipeable regions and
+ * animations on the overview state that depend on the recents implementation.
+ */
+public final class PortraitOverviewStateTouchHelper {
+
+ public PortraitOverviewStateTouchHelper(Launcher launcher) {}
+
+ /**
+ * Whether or not {@link PortraitStatesTouchController} should intercept the touch when on the
+ * overview state.
+ *
+ * @param ev the motion event
+ * @return true if we should intercept the motion event
+ */
+ boolean canInterceptTouch(MotionEvent ev) {
+ // Go does not support swiping to all-apps from recents.
+ return false;
+ }
+
+ /**
+ * Whether or not swiping down to leave overview state should return to the currently running
+ * task app.
+ *
+ * @return true if going back should take the user to the currently running task
+ */
+ boolean shouldSwipeDownReturnToApp() {
+ // Go does not support swiping tasks down to launch tasks from recents.
+ return false;
+ }
+
+ /**
+ * Create the animation for going from overview to the task app via swiping.
+ *
+ * @param duration how long the animation should be
+ * @return the animation
+ */
+ PendingAnimation createSwipeDownToTaskAppAnimation(long duration) {
+ // Go does not support swiping tasks down to launch tasks from recents.
+ return null;
+ }
+}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
new file mode 100644
index 0000000..d0c255c
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import static android.view.View.VISIBLE;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+
+import android.view.View;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.util.TouchController;
+
+/**
+ * Provides recents-related {@link UiFactory} logic and classes.
+ */
+public final class RecentsUiFactory {
+
+ // Scale recents takes before animating in
+ private static final float RECENTS_PREPARE_SCALE = 1.33f;
+
+ private RecentsUiFactory() {}
+
+ /**
+ * Creates and returns a touch controller for swiping recents tasks.
+ *
+ * @param launcher the launcher activity
+ * @return the touch controller for recents tasks
+ */
+ public static TouchController createTaskSwipeController(Launcher launcher) {
+ // We leave all input handling to the view itself.
+ return null;
+ }
+
+ /**
+ * Creates and returns the controller responsible for recents view state transitions.
+ *
+ * @param launcher the launcher activity
+ * @return state handler for recents
+ */
+ public static StateHandler createRecentsViewStateController(Launcher launcher) {
+ return new RecentsViewStateController(launcher);
+ }
+
+ /**
+ * Prepare the recents view to animate in.
+ *
+ * @param launcher the launcher activity
+ */
+ public static void prepareToShowRecents(Launcher launcher) {
+ View overview = launcher.getOverviewPanel();
+ if (overview.getVisibility() != VISIBLE) {
+ SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
+ }
+ }
+
+ /**
+ * Clean-up logic that occurs when recents is no longer in use/visible.
+ *
+ * @param launcher the launcher activity
+ */
+ public static void resetRecents(Launcher launcher) {}
+
+ /**
+ * Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
+ *
+ * @param launcher the launcher activity
+ */
+ public static void onLauncherStateOrResumeChanged(Launcher launcher) {}
+}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
new file mode 100644
index 0000000..f1cb75b
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides;
+
+import static com.android.quickstep.views.IconRecentsView.CONTENT_ALPHA;
+import static com.android.quickstep.views.IconRecentsView.TRANSLATION_Y_FACTOR;
+
+import android.util.FloatProperty;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.Launcher;
+import com.android.quickstep.views.IconRecentsView;
+
+/**
+ * State handler for Go's {@link IconRecentsView}.
+ */
+public final class RecentsViewStateController extends
+ BaseRecentsViewStateController<IconRecentsView> {
+
+ public RecentsViewStateController(@NonNull Launcher launcher) {
+ super(launcher);
+ }
+
+ @Override
+ FloatProperty<IconRecentsView> getTranslationYFactorProperty() {
+ return TRANSLATION_Y_FACTOR;
+ }
+
+ @Override
+ FloatProperty<IconRecentsView> getContentAlphaProperty() {
+ return CONTENT_ALPHA;
+ }
+}
diff --git a/go/quickstep/src/com/android/quickstep/IconRecentsFragment.java b/go/quickstep/src/com/android/quickstep/IconRecentsFragment.java
new file mode 100644
index 0000000..facf0d2
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/IconRecentsFragment.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+
+public class IconRecentsFragment extends Fragment {
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.icon_recents_root_view, container, false);
+ }
+}
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
new file mode 100644
index 0000000..e4741e9
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.view.ViewDebug;
+import android.widget.FrameLayout;
+
+/**
+ * Root view for the icon recents view.
+ */
+public final class IconRecentsView extends FrameLayout {
+
+ public static final FloatProperty<IconRecentsView> TRANSLATION_Y_FACTOR =
+ new FloatProperty<IconRecentsView>("translationYFactor") {
+
+ @Override
+ public void setValue(IconRecentsView view, float v) {
+ view.setTranslationYFactor(v);
+ }
+
+ @Override
+ public Float get(IconRecentsView view) {
+ return view.mTranslationYFactor;
+ }
+ };
+
+ public static final FloatProperty<IconRecentsView> CONTENT_ALPHA =
+ new FloatProperty<IconRecentsView>("contentAlpha") {
+ @Override
+ public void setValue(IconRecentsView view, float v) {
+ ALPHA.set(view, v);
+ }
+
+ @Override
+ public Float get(IconRecentsView view) {
+ return ALPHA.get(view);
+ }
+ };
+
+ /**
+ * A ratio representing the view's relative placement within its padded space. For example, 0
+ * is top aligned and 0.5 is centered vertically.
+ */
+ @ViewDebug.ExportedProperty(category = "launcher")
+ private float mTranslationYFactor;
+
+ public IconRecentsView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setTranslationYFactor(float translationFactor) {
+ mTranslationYFactor = translationFactor;
+ setTranslationY(computeTranslationYForFactor(mTranslationYFactor));
+ }
+
+ private float computeTranslationYForFactor(float translationYFactor) {
+ return translationYFactor * (getPaddingBottom() - getPaddingTop());
+ }
+}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
index 882af59..af1b353 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
@@ -253,19 +253,24 @@
badge.draw(target);
}
+ private Bitmap createIconBitmap(Drawable icon, float scale) {
+ return createIconBitmap(icon, scale, mIconBitmapSize);
+ }
+
/**
+ * @param icon drawable that should be flattened to a bitmap
* @param scale the scale to apply before drawing {@param icon} on the canvas
*/
- private Bitmap createIconBitmap(Drawable icon, float scale) {
- Bitmap bitmap = Bitmap.createBitmap(mIconBitmapSize, mIconBitmapSize,
- Bitmap.Config.ARGB_8888);
+ public Bitmap createIconBitmap(Drawable icon, float scale, int size) {
+ Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+
mCanvas.setBitmap(bitmap);
mOldBounds.set(icon.getBounds());
if (ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
- int offset = Math.max((int) Math.ceil(BLUR_FACTOR * mIconBitmapSize),
- Math.round(mIconBitmapSize * (1 - scale) / 2 ));
- icon.setBounds(offset, offset, mIconBitmapSize - offset, mIconBitmapSize - offset);
+ int offset = Math.max((int) Math.ceil(BLUR_FACTOR * size),
+ Math.round(size * (1 - scale) / 2 ));
+ icon.setBounds(offset, offset, size - offset, size - offset);
icon.draw(mCanvas);
} else {
if (icon instanceof BitmapDrawable) {
@@ -275,8 +280,8 @@
bitmapDrawable.setTargetDensity(mContext.getResources().getDisplayMetrics());
}
}
- int width = mIconBitmapSize;
- int height = mIconBitmapSize;
+ int width = size;
+ int height = size;
int intrinsicWidth = icon.getIntrinsicWidth();
int intrinsicHeight = icon.getIntrinsicHeight();
@@ -289,11 +294,11 @@
width = (int) (height * ratio);
}
}
- final int left = (mIconBitmapSize - width) / 2;
- final int top = (mIconBitmapSize - height) / 2;
+ final int left = (size - width) / 2;
+ final int top = (size - height) / 2;
icon.setBounds(left, top, left + width, top + height);
mCanvas.save();
- mCanvas.scale(scale, scale, mIconBitmapSize / 2, mIconBitmapSize / 2);
+ mCanvas.scale(scale, scale, size / 2, size / 2);
icon.draw(mCanvas);
mCanvas.restore();
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 8af310c..ab97344 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/recents_ui_overrides/res/layout/overview_panel.xml
similarity index 100%
rename from quickstep/res/layout/overview_panel.xml
rename to quickstep/recents_ui_overrides/res/layout/overview_panel.xml
diff --git a/quickstep/res/values/override.xml b/quickstep/recents_ui_overrides/res/values/override.xml
similarity index 95%
rename from quickstep/res/values/override.xml
rename to quickstep/recents_ui_overrides/res/values/override.xml
index d683659..c60cf5a 100644
--- a/quickstep/res/values/override.xml
+++ b/quickstep/recents_ui_overrides/res/values/override.xml
@@ -14,6 +14,8 @@
limitations under the License.
-->
+<!-- Class overrides for launcher with quickstep. -->
+
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_transition_manager_class" translatable="false">com.android.launcher3.LauncherAppTransitionManagerImpl</string>
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
new file mode 100644
index 0000000..9921455
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.TaskUtils.findTaskViewToLaunch;
+import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.Interpolators;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from
+ * {@link RecentsView}.
+ */
+public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransitionManagerImpl {
+
+ private RecentsView mRecentsView;
+
+ public LauncherAppTransitionManagerImpl(Context context) {
+ super(context);
+ mRecentsView = mLauncher.getOverviewPanel();
+ }
+
+ @Override
+ protected boolean isLaunchingFromRecents(@NonNull View v,
+ @Nullable RemoteAnimationTargetCompat[] targets) {
+ return mLauncher.getStateManager().getState().overviewUi
+ && findTaskViewToLaunch(mLauncher, v, targets) != null;
+ }
+
+ @Override
+ protected boolean isQuickSwitchInProgress() {
+ return mRecentsView.getQuickScrubController().isQuickSwitch();
+ }
+
+ @Override
+ protected ActivityOptions getQuickSwitchActivityOptions() {
+ return ActivityOptions.makeCustomAnimation(mLauncher, R.anim.no_anim,
+ R.anim.no_anim);
+ }
+
+ @Override
+ protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
+ @NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ boolean skipLauncherChanges = !launcherClosing;
+ boolean isLaunchingFromQuickscrub =
+ recentsView.getQuickScrubController().isWaitingForTaskLaunch();
+
+ TaskView taskView = findTaskViewToLaunch(mLauncher, v, targets);
+
+ int duration = isLaunchingFromQuickscrub
+ ? RECENTS_QUICKSCRUB_LAUNCH_DURATION
+ : RECENTS_LAUNCH_DURATION;
+
+ ClipAnimationHelper helper = new ClipAnimationHelper(mLauncher);
+ anim.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets, helper)
+ .setDuration(duration));
+
+ Animator childStateAnimation = null;
+ // Found a visible recents task that matches the opening app, lets launch the app from there
+ Animator launcherAnim;
+ final AnimatorListenerAdapter windowAnimEndListener;
+ if (launcherClosing) {
+ launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView, helper);
+ launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
+ launcherAnim.setDuration(duration);
+
+ // Make sure recents gets fixed up by resetting task alphas and scales, etc.
+ windowAnimEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mLauncher.getStateManager().moveToRestState();
+ mLauncher.getStateManager().reapplyState();
+ }
+ };
+ } else {
+ AnimatorPlaybackController controller =
+ mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL, duration);
+ controller.dispatchOnStart();
+ childStateAnimation = controller.getTarget();
+ launcherAnim = controller.getAnimationPlayer().setDuration(duration);
+ windowAnimEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mLauncher.getStateManager().goToState(NORMAL, false);
+ }
+ };
+ }
+ anim.play(launcherAnim);
+
+ // Set the current animation first, before adding windowAnimEndListener. Setting current
+ // animation adds some listeners which need to be called before windowAnimEndListener
+ // (the ordering of listeners matter in this case).
+ mLauncher.getStateManager().setCurrentAnimation(anim, childStateAnimation);
+ anim.addListener(windowAnimEndListener);
+ }
+
+ @Override
+ protected Runnable composeViewContentAnimator(@NonNull AnimatorSet anim, float[] alphas,
+ float[] trans) {
+ RecentsView overview = mLauncher.getOverviewPanel();
+ ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
+ RecentsView.CONTENT_ALPHA, alphas);
+ alpha.setDuration(CONTENT_ALPHA_DURATION);
+ alpha.setInterpolator(LINEAR);
+ anim.play(alpha);
+
+ ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
+ transY.setInterpolator(AGGRESSIVE_EASE);
+ transY.setDuration(CONTENT_TRANSLATION_DURATION);
+ anim.play(transY);
+
+ return mLauncher.getStateManager()::reapplyState;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java
similarity index 94%
rename from quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
rename to quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java
index 8f1d46c..de6f7a7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_QUICKSTEP_PREVIEW;
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
import android.graphics.Rect;
@@ -75,7 +76,11 @@
public void onStateEnabled(Launcher launcher) {
RecentsView rv = launcher.getOverviewPanel();
rv.setOverviewStateEnabled(true);
- AbstractFloatingView.closeAllOpenViewsExcept(launcher, TYPE_QUICKSTEP_PREVIEW);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ AbstractFloatingView.closeAllOpenViews(launcher);
+ } else {
+ AbstractFloatingView.closeAllOpenViewsExcept(launcher, TYPE_QUICKSTEP_PREVIEW);
+ }
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PortraitOverviewStateTouchHelper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PortraitOverviewStateTouchHelper.java
new file mode 100644
index 0000000..eead4c8
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PortraitOverviewStateTouchHelper.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.uioverrides.PortraitStatesTouchController.isTouchOverHotseat;
+
+import android.view.MotionEvent;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.util.PendingAnimation;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+
+/**
+ * Helper class for {@link PortraitStatesTouchController} that determines swipeable regions and
+ * animations on the overview state that depend on the recents implementation.
+ */
+public final class PortraitOverviewStateTouchHelper {
+
+ RecentsView mRecentsView;
+ Launcher mLauncher;
+
+ public PortraitOverviewStateTouchHelper(Launcher launcher) {
+ mLauncher = launcher;
+ mRecentsView = launcher.getOverviewPanel();
+ }
+
+ /**
+ * Whether or not {@link PortraitStatesTouchController} should intercept the touch when on the
+ * overview state.
+ *
+ * @param ev the motion event
+ * @return true if we should intercept the motion event
+ */
+ boolean canInterceptTouch(MotionEvent ev) {
+ if (mRecentsView.getChildCount() > 0) {
+ // Allow swiping up in the gap between the hotseat and overview.
+ return ev.getY() >= mRecentsView.getChildAt(0).getBottom();
+ } else {
+ // If there are no tasks, we only intercept if we're below the hotseat height.
+ return isTouchOverHotseat(mLauncher, ev);
+ }
+ }
+
+ /**
+ * Whether or not swiping down to leave overview state should return to the currently running
+ * task app.
+ *
+ * @return true if going back should take the user to the currently running task
+ */
+ boolean shouldSwipeDownReturnToApp() {
+ TaskView taskView = mRecentsView.getTaskViewAt(mRecentsView.getNextPage());
+ return taskView != null && mRecentsView.shouldSwipeDownLaunchApp();
+ }
+
+ /**
+ * Create the animation for going from overview to the task app via swiping. Should only be
+ * called when {@link #shouldSwipeDownReturnToApp()} returns true.
+ *
+ * @param duration how long the animation should be
+ * @return the animation
+ */
+ PendingAnimation createSwipeDownToTaskAppAnimation(long duration) {
+ TaskView taskView = mRecentsView.getTaskViewAt(mRecentsView.getNextPage());
+ if (taskView == null) {
+ throw new IllegalStateException("There is no task view to animate to.");
+ }
+ return mRecentsView.createTaskLauncherAnimation(taskView, duration);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
new file mode 100644
index 0000000..f18f43c
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import static android.view.View.VISIBLE;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.util.TouchController;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * Provides recents-related {@link UiFactory} logic and classes.
+ */
+public final class RecentsUiFactory {
+
+ // Scale recents takes before animating in
+ private static final float RECENTS_PREPARE_SCALE = 1.33f;
+
+ private RecentsUiFactory() {}
+
+ /**
+ * Creates and returns a touch controller for swiping recents tasks.
+ *
+ * @param launcher the launcher activity
+ * @return the touch controller for recents tasks
+ */
+ public static TouchController createTaskSwipeController(Launcher launcher) {
+ return new LauncherTaskViewController(launcher);
+ }
+
+ /**
+ * Creates and returns the controller responsible for recents view state transitions.
+ *
+ * @param launcher the launcher activity
+ * @return state handler for recents
+ */
+ public static StateHandler createRecentsViewStateController(Launcher launcher) {
+ return new RecentsViewStateController(launcher);
+ }
+
+ /**
+ * Prepare the recents view to animate in.
+ *
+ * @param launcher the launcher activity
+ */
+ public static void prepareToShowRecents(Launcher launcher) {
+ RecentsView overview = launcher.getOverviewPanel();
+ if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
+ SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
+ }
+ }
+
+ /**
+ * Clean-up logic that occurs when recents is no longer in use/visible.
+ *
+ * @param launcher the launcher activity
+ */
+ public static void resetRecents(Launcher launcher) {
+ RecentsView recents = launcher.getOverviewPanel();
+ recents.reset();
+ }
+
+ /**
+ * Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
+ *
+ * @param launcher the launcher activity
+ */
+ public static void onLauncherStateOrResumeChanged(Launcher launcher) {
+ LauncherState state = launcher.getStateManager().getState();
+ if (state == NORMAL) {
+ launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
+ }
+ }
+
+ private static final class LauncherTaskViewController extends
+ TaskViewTouchController<Launcher> {
+
+ LauncherTaskViewController(Launcher activity) {
+ super(activity);
+ }
+
+ @Override
+ protected boolean isRecentsInteractive() {
+ return mActivity.isInState(OVERVIEW);
+ }
+
+ @Override
+ protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
+ mActivity.getStateManager().setCurrentUserControlledAnimation(animController);
+ }
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
new file mode 100644
index 0000000..7d7946d
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -0,0 +1,102 @@
+/*
+ * 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.uioverrides;
+
+import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR;
+import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
+import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
+import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
+
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.util.FloatProperty;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.AnimationConfig;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.Interpolators;
+import com.android.quickstep.views.LauncherRecentsView;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * State handler for handling UI changes for {@link LauncherRecentsView}. In addition to managing
+ * the basic view properties, this class also manages changes in the task visuals.
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public final class RecentsViewStateController extends
+ BaseRecentsViewStateController<LauncherRecentsView> {
+
+ public RecentsViewStateController(Launcher launcher) {
+ super(launcher);
+ }
+
+ @Override
+ public void setState(@NonNull LauncherState state) {
+ super.setState(state);
+ if (state.overviewUi) {
+ mRecentsView.updateEmptyMessage();
+ mRecentsView.resetTaskVisuals();
+ }
+ }
+
+ @Override
+ void setStateWithAnimationInternal(@NonNull final LauncherState toState,
+ @NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) {
+ super.setStateWithAnimationInternal(toState, builder, config);
+
+ if (!toState.overviewUi) {
+ builder.addOnFinishRunnable(mRecentsView::resetTaskVisuals);
+ }
+
+ if (toState.overviewUi) {
+ ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1);
+ updateAnim.addUpdateListener(valueAnimator -> {
+ // While animating into recents, update the visible task data as needed
+ mRecentsView.loadVisibleTaskData();
+ });
+ updateAnim.setDuration(config.duration);
+ builder.play(updateAnim);
+ mRecentsView.updateEmptyMessage();
+ }
+ }
+
+ @Override
+ Interpolator getScaleAndTransYInterpolator(@NonNull LauncherState toState,
+ @NonNull AnimatorSetBuilder builder) {
+ if (mLauncher.getStateManager().getState() == OVERVIEW && toState == FAST_OVERVIEW) {
+ return Interpolators.clampToProgress(QUICK_SCRUB_START_INTERPOLATOR, 0,
+ QUICK_SCRUB_TRANSLATION_Y_FACTOR);
+ }
+ return super.getScaleAndTransYInterpolator(toState, builder);
+ }
+
+ @Override
+ FloatProperty<LauncherRecentsView> getTranslationYFactorProperty() {
+ return TRANSLATION_Y_FACTOR;
+ }
+
+ @Override
+ FloatProperty<RecentsView> getContentAlphaProperty() {
+ return CONTENT_ALPHA;
+ }
+}
diff --git a/quickstep/res/drawable/bg_wellbeing_toast.xml b/quickstep/res/drawable/bg_wellbeing_toast.xml
new file mode 100644
index 0000000..22d6f8a
--- /dev/null
+++ b/quickstep/res/drawable/bg_wellbeing_toast.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <solid android:color="#E61A73E8" />
+ <corners android:radius="@dimen/task_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/hourglass_bottom.xml b/quickstep/res/drawable/hourglass_bottom.xml
new file mode 100644
index 0000000..b5ef008
--- /dev/null
+++ b/quickstep/res/drawable/hourglass_bottom.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <group>
+ <clip-path android:pathData="M0,0H24V24H0Z M 0,0"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M6,2V8H6l4,4L6,16H6v6H18V16h0l-4,-4,4,-4h0V2Zm6,9.5,-4,-4V4h8V7.5Z"/>
+ </group>
+</vector>
diff --git a/quickstep/res/drawable/hourglass_top.xml b/quickstep/res/drawable/hourglass_top.xml
new file mode 100644
index 0000000..7fc77d3
--- /dev/null
+++ b/quickstep/res/drawable/hourglass_top.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <group>
+ <clip-path android:pathData="M0,0H24V24H0Z M 0,0"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M6,2V8H6l4,4L6,16H6v6H18V16h0l-4,-4,4,-4h0V2ZM16,16.5V20H8V16.5l4,-4Z"/>
+ </group>
+</vector>
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 43ae9b8..f96a66f 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -38,18 +38,26 @@
<com.android.quickstep.views.DigitalWellBeingToast
android:id="@+id/digital_well_being_toast"
android:layout_width="match_parent"
- android:layout_height="100dp"
+ android:layout_height="48dp"
android:importantForAccessibility="noHideDescendants"
- android:background="#800000FF"
+ android:background="@drawable/bg_wellbeing_toast"
android:layout_gravity="bottom"
- android:visibility="gone"
- >
+ android:gravity="center"
+ android:visibility="gone">
+ <ImageView
+ android:id="@+id/digital_well_being_hourglass"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="8dp"
+ />
<TextView
- android:id="@+id/remaining_time"
+ android:id="@+id/digital_well_being_remaining_time"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
+ android:layout_height="24dp"
+ android:fontFamily="sans-serif"
+ android:textSize="14sp"
android:textColor="@android:color/white"
+ android:gravity="center_vertical"
/>
</com.android.quickstep.views.DigitalWellBeingToast>
</com.android.quickstep.views.TaskView>
\ No newline at end of file
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 08132aa..0c741a1 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-->
-<resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Application name -->
<string name="derived_app_name" translatable="false">Quickstep</string>
@@ -46,4 +46,20 @@
<!-- Accessibility title for the list of recent apps [CHAR_LIMIT=none] -->
<string name="accessibility_recent_apps">Recent apps</string>
+
+ <!-- Accessibility title for an app card in Recents for apps that have time limit set
+ [CHAR_LIMIT=none] -->
+ <string name="task_contents_description_with_remaining_time"><xliff:g id="task_description" example="GMail">%1$s</xliff:g>, <xliff:g id="remaining_time" example="7 minutes left today">%2$s</xliff:g></string>
+
+ <!-- Text to show total app usage per day if it is less than 1 minute ("<" is the
+ escaped form of '<'). [CHAR LIMIT=10] -->
+ <string name="shorter_duration_less_than_one_minute">< 1 minute</string>
+
+ <!-- Annotation shown on an app card in Recents, telling that the app was switched to a
+ grayscale because it ran over its time limit [CHAR LIMIT=25] -->
+ <string name="app_in_grayscale">App in grayscale</string>
+
+ <!-- Annotation shown on an app card in Recents, telling that the app has a usage limit set by
+ the user, and a given time is left for it today [CHAR LIMIT=20] -->
+ <string name="time_left_for_app"><xliff:g id="time" example="7 minutes">%1$s</xliff:g> left today</string>
</resources>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index 08b6bfc..c5c5323 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -44,8 +44,8 @@
@Override
protected boolean init(Launcher launcher, boolean alreadyOnHome) {
if (mRemoteAnimationProvider != null) {
- LauncherAppTransitionManagerImpl appTransitionManager =
- (LauncherAppTransitionManagerImpl) launcher.getAppTransitionManager();
+ QuickstepAppTransitionManagerImpl appTransitionManager =
+ (QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
// Set a one-time animation provider. After the first call, this will get cleared.
// TODO: Probably also check the intended target id.
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
similarity index 79%
rename from quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
rename to quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 11b5fea..07a5b72 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
@@ -30,8 +29,6 @@
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
-import static com.android.quickstep.TaskUtils.findTaskViewToLaunch;
-import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -57,10 +54,12 @@
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.graphics.DrawableFactory;
@@ -68,12 +67,9 @@
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.quickstep.RecentsModel;
-import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteAnimationTargetSet;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityCompat;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
@@ -85,14 +81,15 @@
import com.android.systemui.shared.system.WindowManagerWrapper;
/**
- * Manages the opening and closing app transitions from Launcher.
+ * {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
+ * home and/or all-apps.
*/
@TargetApi(Build.VERSION_CODES.O)
@SuppressWarnings("unused")
-public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManager
+public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTransitionManager
implements OnDeviceProfileChangeListener {
- private static final String TAG = "LauncherTransition";
+ private static final String TAG = "QuickstepTransition";
/** Duration of status bar animations. */
public static final int STATUS_BAR_TRANSITION_DURATION = 120;
@@ -119,10 +116,14 @@
private static final int LAUNCHER_RESUME_START_DELAY = 100;
private static final int CLOSING_TRANSITION_DURATION_MS = 250;
+ protected static final int CONTENT_ALPHA_DURATION = 217;
+ protected static final int CONTENT_TRANSLATION_DURATION = 350;
+
// Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down.
public static final float ALL_APPS_PROGRESS_OFF_SCREEN = 1.3059858f;
- private final Launcher mLauncher;
+ protected final Launcher mLauncher;
+
private final DragLayer mDragLayer;
private final AlphaProperty mDragLayerAlpha;
@@ -150,7 +151,7 @@
}
};
- public LauncherAppTransitionManagerImpl(Context context) {
+ public QuickstepAppTransitionManagerImpl(Context context) {
mLauncher = Launcher.getLauncher(context);
mDragLayer = mLauncher.getDragLayer();
mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
@@ -179,12 +180,9 @@
@Override
public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
if (hasControlRemoteAppTransitionPermission()) {
- boolean fromRecents = mLauncher.getStateManager().getState().overviewUi
- && findTaskViewToLaunch(launcher, v, null) != null;
- RecentsView recentsView = mLauncher.getOverviewPanel();
- if (fromRecents && recentsView.getQuickScrubController().isQuickSwitch()) {
- return ActivityOptions.makeCustomAnimation(mLauncher, R.anim.no_anim,
- R.anim.no_anim);
+ boolean fromRecents = isLaunchingFromRecents(v, null /* targets */);
+ if (fromRecents && isQuickSwitchInProgress()) {
+ return getQuickSwitchActivityOptions();
}
RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler,
@@ -198,33 +196,10 @@
boolean launcherClosing =
launcherIsATargetWithMode(targetCompats, MODE_CLOSING);
- if (!composeRecentsLaunchAnimator(v, targetCompats, anim)) {
- // Set the state animation first so that any state listeners are called
- // before our internal listeners.
- mLauncher.getStateManager().setCurrentAnimation(anim);
-
- Rect windowTargetBounds = getWindowTargetBounds(targetCompats);
- boolean isAllOpeningTargetTrs = true;
- for (int i = 0; i < targetCompats.length; i++) {
- RemoteAnimationTargetCompat target = targetCompats[i];
- if (target.mode == MODE_OPENING) {
- isAllOpeningTargetTrs &= target.isTranslucent;
- }
- if (!isAllOpeningTargetTrs) break;
- }
- playIconAnimators(anim, v, windowTargetBounds, !isAllOpeningTargetTrs);
- if (launcherClosing) {
- Pair<AnimatorSet, Runnable> launcherContentAnimator =
- getLauncherContentAnimator(true /* isAppOpening */);
- anim.play(launcherContentAnimator.first);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- launcherContentAnimator.second.run();
- }
- });
- }
- anim.play(getOpeningWindowAnimators(v, targetCompats, windowTargetBounds));
+ if (isLaunchingFromRecents(v, targetCompats)) {
+ composeRecentsLaunchAnimator(anim, v, targetCompats, launcherClosing);
+ } else {
+ composeIconLaunchAnimator(anim, v, targetCompats, launcherClosing);
}
if (launcherClosing) {
@@ -235,6 +210,8 @@
}
};
+ // Note that this duration is a guess as we do not know if the animation will be a
+ // recents launch or not for sure until we know the opening app targets.
int duration = fromRecents
? RECENTS_LAUNCH_DURATION
: APP_LAUNCH_DURATION;
@@ -248,6 +225,83 @@
}
/**
+ * Whether the launch is a recents app transition and we should do a launch animation
+ * from the recents view. Note that if the remote animation targets are not provided, this
+ * may not always be correct as we may resolve the opening app to a task when the animation
+ * starts.
+ *
+ * @param v the view to launch from
+ * @param targets apps that are opening/closing
+ * @return true if the app is launching from recents, false if it most likely is not
+ */
+ protected abstract boolean isLaunchingFromRecents(@NonNull View v,
+ @Nullable RemoteAnimationTargetCompat[] targets);
+
+ /**
+ * Whether a quick scrub is in progress.
+ *
+ * @return true if in progress
+ */
+ protected abstract boolean isQuickSwitchInProgress();
+
+ /**
+ * Get activity options for a quick switch launch that include the launch animation.
+ *
+ * @return the activity options for a quick switch recents launch
+ */
+ protected abstract ActivityOptions getQuickSwitchActivityOptions();
+
+ /**
+ * Composes the animations for a launch from the recents list.
+ *
+ * @param anim the animator set to add to
+ * @param v the launching view
+ * @param targets the apps that are opening/closing
+ * @param launcherClosing true if the launcher app is closing
+ */
+ protected abstract void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
+ @NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing);
+
+ /**
+ * Compose the animations for a launch from the app icon.
+ *
+ * @param anim the animation to add to
+ * @param v the launching view with the icon
+ * @param targets the list of opening/closing apps
+ * @param launcherClosing true if launcher is closing
+ */
+ private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
+ @NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
+ // Set the state animation first so that any state listeners are called
+ // before our internal listeners.
+ mLauncher.getStateManager().setCurrentAnimation(anim);
+
+ Rect windowTargetBounds = getWindowTargetBounds(targets);
+ boolean isAllOpeningTargetTrs = true;
+ for (int i = 0; i < targets.length; i++) {
+ RemoteAnimationTargetCompat target = targets[i];
+ if (target.mode == MODE_OPENING) {
+ isAllOpeningTargetTrs &= target.isTranslucent;
+ }
+ if (!isAllOpeningTargetTrs) break;
+ }
+ playIconAnimators(anim, v, windowTargetBounds, !isAllOpeningTargetTrs);
+ if (launcherClosing) {
+ Pair<AnimatorSet, Runnable> launcherContentAnimator =
+ getLauncherContentAnimator(true /* isAppOpening */,
+ new float[] {0, mContentTransY});
+ anim.play(launcherContentAnimator.first);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ launcherContentAnimator.second.run();
+ }
+ });
+ }
+ anim.play(getOpeningWindowAnimators(v, targets, windowTargetBounds));
+ }
+
+ /**
* Return the window bounds of the opening target.
* In multiwindow mode, we need to get the final size of the opening app window target to help
* figure out where the floating view should animate to.
@@ -277,90 +331,20 @@
}
/**
- * Composes the animations for a launch from the recents list if possible.
- */
- private boolean composeRecentsLaunchAnimator(View v,
- RemoteAnimationTargetCompat[] targets, AnimatorSet target) {
- // Ensure recents is actually visible
- if (!mLauncher.getStateManager().getState().overviewUi) {
- return false;
- }
-
- RecentsView recentsView = mLauncher.getOverviewPanel();
- boolean launcherClosing = launcherIsATargetWithMode(targets, MODE_CLOSING);
- boolean skipLauncherChanges = !launcherClosing;
- boolean isLaunchingFromQuickscrub =
- recentsView.getQuickScrubController().isWaitingForTaskLaunch();
-
- TaskView taskView = findTaskViewToLaunch(mLauncher, v, targets);
- if (taskView == null) {
- return false;
- }
-
- int duration = isLaunchingFromQuickscrub
- ? RECENTS_QUICKSCRUB_LAUNCH_DURATION
- : RECENTS_LAUNCH_DURATION;
-
- ClipAnimationHelper helper = new ClipAnimationHelper(mLauncher);
- target.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets, helper)
- .setDuration(duration));
-
- Animator childStateAnimation = null;
- // Found a visible recents task that matches the opening app, lets launch the app from there
- Animator launcherAnim;
- final AnimatorListenerAdapter windowAnimEndListener;
- if (launcherClosing) {
- launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView, helper);
- launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
- launcherAnim.setDuration(duration);
-
- // Make sure recents gets fixed up by resetting task alphas and scales, etc.
- windowAnimEndListener = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mLauncher.getStateManager().moveToRestState();
- mLauncher.getStateManager().reapplyState();
- }
- };
- } else {
- AnimatorPlaybackController controller =
- mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL, duration);
- controller.dispatchOnStart();
- childStateAnimation = controller.getTarget();
- launcherAnim = controller.getAnimationPlayer().setDuration(duration);
- windowAnimEndListener = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mLauncher.getStateManager().goToState(NORMAL, false);
- }
- };
- }
- target.play(launcherAnim);
-
- // Set the current animation first, before adding windowAnimEndListener. Setting current
- // animation adds some listeners which need to be called before windowAnimEndListener
- // (the ordering of listeners matter in this case).
- mLauncher.getStateManager().setCurrentAnimation(target, childStateAnimation);
- target.addListener(windowAnimEndListener);
- return true;
- }
-
- /**
* Content is everything on screen except the background and the floating view (if any).
*
* @param isAppOpening True when this is called when an app is opening.
* False when this is called when an app is closing.
+ * @param trans Array that contains the start and end translation values for the content.
*/
- private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean isAppOpening) {
+ private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean isAppOpening,
+ float[] trans) {
AnimatorSet launcherAnimator = new AnimatorSet();
Runnable endListener;
float[] alphas = isAppOpening
? new float[] {1, 0}
: new float[] {0, 1};
- float[] trans = isAppOpening
- ? new float[] {0, mContentTransY}
- : new float[] {-mContentTransY, 0};
if (mLauncher.isInState(ALL_APPS)) {
// All Apps in portrait mode is full screen, so we only animate AllAppsContainerView.
@@ -371,7 +355,7 @@
appsView.setTranslationY(trans[0]);
ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
- alpha.setDuration(217);
+ alpha.setDuration(CONTENT_ALPHA_DURATION);
alpha.setInterpolator(LINEAR);
appsView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
alpha.addListener(new AnimatorListenerAdapter() {
@@ -382,7 +366,7 @@
});
ObjectAnimator transY = ObjectAnimator.ofFloat(appsView, View.TRANSLATION_Y, trans);
transY.setInterpolator(AGGRESSIVE_EASE);
- transY.setDuration(350);
+ transY.setDuration(CONTENT_TRANSLATION_DURATION);
launcherAnimator.play(alpha);
launcherAnimator.play(transY);
@@ -396,32 +380,19 @@
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
launcherAnimator.play(ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS,
allAppsController.getProgress(), ALL_APPS_PROGRESS_OFF_SCREEN));
-
- RecentsView overview = mLauncher.getOverviewPanel();
- ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
- RecentsView.CONTENT_ALPHA, alphas);
- alpha.setDuration(217);
- alpha.setInterpolator(LINEAR);
- launcherAnimator.play(alpha);
-
- ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
- transY.setInterpolator(AGGRESSIVE_EASE);
- transY.setDuration(350);
- launcherAnimator.play(transY);
-
- endListener = mLauncher.getStateManager()::reapplyState;
+ endListener = composeViewContentAnimator(launcherAnimator, alphas, trans);
} else {
mDragLayerAlpha.setValue(alphas[0]);
ObjectAnimator alpha =
ObjectAnimator.ofFloat(mDragLayerAlpha, MultiValueAlpha.VALUE, alphas);
- alpha.setDuration(217);
+ alpha.setDuration(CONTENT_ALPHA_DURATION);
alpha.setInterpolator(LINEAR);
launcherAnimator.play(alpha);
mDragLayer.setTranslationY(trans[0]);
ObjectAnimator transY = ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y, trans);
transY.setInterpolator(AGGRESSIVE_EASE);
- transY.setDuration(350);
+ transY.setDuration(CONTENT_TRANSLATION_DURATION);
launcherAnimator.play(transY);
mDragLayer.getScrim().hideSysUiScrim(true);
@@ -429,21 +400,38 @@
mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- endListener = () -> {
- resetContentView();
- mDragLayer.getScrim().hideSysUiScrim(false);
- };
+ endListener = this::resetContentView;
}
return new Pair<>(launcherAnimator, endListener);
}
/**
+ * Compose recents view alpha and translation Y animation when launcher opens/closes apps.
+ *
+ * @param anim the animator set to add to
+ * @param alphas the alphas to animate to over time
+ * @param trans the translation Y values to animator to over time
+ * @return listener to run when the animation ends
+ */
+ protected abstract Runnable composeViewContentAnimator(@NonNull AnimatorSet anim,
+ float[] alphas, float[] trans);
+
+ /**
* Animators for the "floating view" of the view used to launch the target.
*/
private void playIconAnimators(AnimatorSet appOpenAnimator, View v, Rect windowTargetBounds,
boolean toggleVisibility) {
final boolean isBubbleTextView = v instanceof BubbleTextView;
- mFloatingView = new View(mLauncher);
+ if (mFloatingView == null) {
+ mFloatingView = new View(mLauncher);
+ } else {
+ mFloatingView.setTranslationX(0);
+ mFloatingView.setTranslationY(0);
+ mFloatingView.setScaleX(1);
+ mFloatingView.setScaleY(1);
+ mFloatingView.setAlpha(1);
+ mFloatingView.setBackground(null);
+ }
if (isBubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
// Create a copy of the app icon
mFloatingView.setBackground(DrawableFactory.INSTANCE.get(mLauncher)
@@ -481,19 +469,17 @@
: viewLocationLeft;
LayoutParams lp = new LayoutParams(rect.width(), rect.height());
lp.ignoreInsets = true;
- lp.setMarginStart(viewLocationStart);
+ lp.leftMargin = viewLocationStart;
lp.topMargin = viewLocationTop;
mFloatingView.setLayoutParams(lp);
// Set the properties here already to make sure they'are available when running the first
// animation frame.
- mFloatingView.setLeft(viewLocationLeft);
- mFloatingView.setTop(viewLocationTop);
- mFloatingView.setRight(viewLocationLeft + rect.width());
- mFloatingView.setBottom(viewLocationTop + rect.height());
+ mFloatingView.layout(viewLocationLeft, viewLocationTop,
+ viewLocationLeft + rect.width(), viewLocationTop + rect.height());
// Swap the two views in place.
- ((ViewGroup) mDragLayer.getParent()).addView(mFloatingView);
+ ((ViewGroup) mDragLayer.getParent()).getOverlay().add(mFloatingView);
if (toggleVisibility) {
v.setVisibility(View.INVISIBLE);
}
@@ -562,7 +548,7 @@
((BubbleTextView) v).setStayPressed(false);
}
v.setVisibility(View.VISIBLE);
- ((ViewGroup) mDragLayer.getParent()).removeView(mFloatingView);
+ ((ViewGroup) mDragLayer.getParent()).getOverlay().remove(mFloatingView);
}
});
}
@@ -684,10 +670,13 @@
RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
- new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(),
+ new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(false /* fromUnlock */),
CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
- // TODO: Transition for unlock to home TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER
+ definition.addRemoteAnimation(
+ WindowManagerWrapper.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(true /* fromUnlock */),
+ CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
}
}
@@ -700,7 +689,7 @@
* @return Runner that plays when user goes to Launcher
* ie. pressing home, swiping up from nav bar.
*/
- private RemoteAnimationRunnerCompat getWallpaperOpenRunner() {
+ private RemoteAnimationRunnerCompat getWallpaperOpenRunner(boolean fromUnlock) {
return new LauncherAnimationRunner(mHandler, false /* startAtFrontOfQueue */) {
@Override
public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
@@ -726,7 +715,9 @@
if (anim == null) {
anim = new AnimatorSet();
- anim.play(getClosingWindowAnimators(targetCompats));
+ anim.play(fromUnlock
+ ? getUnlockWindowAnimator(targetCompats)
+ : getClosingWindowAnimators(targetCompats));
// Normally, we run the launcher content animation when we are transitioning
// home, but if home is already visible, then we don't want to animate the
@@ -740,7 +731,21 @@
|| mLauncher.isForceInvisible()) {
// Only register the content animation for cancellation when state changes
mLauncher.getStateManager().setCurrentAnimation(anim);
- createLauncherResumeAnimation(anim);
+ if (fromUnlock) {
+ Pair<AnimatorSet, Runnable> contentAnimator =
+ getLauncherContentAnimator(false /* isAppOpening */,
+ new float[] {mContentTransY, 0});
+ contentAnimator.first.setStartDelay(0);
+ anim.play(contentAnimator.first);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ contentAnimator.second.run();
+ }
+ });
+ } else {
+ createLauncherResumeAnimation(anim);
+ }
}
}
@@ -751,6 +756,31 @@
}
/**
+ * Animator that controls the transformations of the windows when unlocking the device.
+ */
+ private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] targets) {
+ SyncRtSurfaceTransactionApplierCompat surfaceApplier =
+ new SyncRtSurfaceTransactionApplierCompat(mDragLayer);
+ ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1);
+ unlockAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
+ float cornerRadius = RecentsModel.INSTANCE.get(mLauncher).getWindowCornerRadius();
+ unlockAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ SurfaceParams[] params = new SurfaceParams[targets.length];
+ for (int i = targets.length - 1; i >= 0; i--) {
+ RemoteAnimationTargetCompat target = targets[i];
+ params[i] = new SurfaceParams(target.leash, 1f, null,
+ target.sourceContainerBounds,
+ RemoteAnimationProvider.getLayer(target, MODE_OPENING), cornerRadius);
+ }
+ surfaceApplier.scheduleApply(params);
+ }
+ });
+ return unlockAnimator;
+ }
+
+ /**
* Animator that controls the transformations of the windows the targets that are closing.
*/
private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] targets) {
@@ -804,7 +834,8 @@
private void createLauncherResumeAnimation(AnimatorSet anim) {
if (mLauncher.isInState(LauncherState.ALL_APPS)) {
Pair<AnimatorSet, Runnable> contentAnimator =
- getLauncherContentAnimator(false /* isAppOpening */);
+ getLauncherContentAnimator(false /* isAppOpening */,
+ new float[] {-mContentTransY, 0});
contentAnimator.first.setStartDelay(LAUNCHER_RESUME_START_DELAY);
anim.play(contentAnimator.first);
anim.addListener(new AnimatorListenerAdapter() {
@@ -828,6 +859,8 @@
workspaceAnimator.setDuration(333);
workspaceAnimator.setInterpolator(Interpolators.DEACCEL_1_7);
+ mDragLayer.getScrim().hideSysUiScrim(true);
+
// Pause page indicator animations as they lead to layer trashing.
mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
@@ -847,6 +880,7 @@
mDragLayerAlpha.setValue(1f);
mDragLayer.setLayerType(View.LAYER_TYPE_NONE, null);
mDragLayer.setTranslationY(0f);
+ mDragLayer.getScrim().hideSysUiScrim(false);
}
private boolean hasControlRemoteAppTransitionPermission() {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
index 1906286..25e0af2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_QUICKSTEP_PREVIEW;
import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
@@ -45,7 +46,11 @@
@Override
public void onStateEnabled(Launcher launcher) {
- AbstractFloatingView.closeAllOpenViewsExcept(launcher, TYPE_QUICKSTEP_PREVIEW);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ AbstractFloatingView.closeAllOpenViews(launcher);
+ } else {
+ AbstractFloatingView.closeAllOpenViewsExcept(launcher, TYPE_QUICKSTEP_PREVIEW);
+ }
dispatchWindowStateChanged(launcher);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java
index fdb13b1..8d28f33 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java
@@ -41,7 +41,8 @@
if (launcher.getDeviceProfile().isVerticalBarLayout()) {
return super.getVerticalProgress(launcher);
}
- int transitionLength = LayoutUtils.getShelfTrackingDistance(launcher.getDeviceProfile());
+ int transitionLength = LayoutUtils.getShelfTrackingDistance(launcher,
+ launcher.getDeviceProfile());
AllAppsTransitionController controller = launcher.getAllAppsController();
float scrollRange = Math.max(controller.getShiftRange(), 1);
float progressDelta = (transitionLength / scrollRange);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
new file mode 100644
index 0000000..df9dbe4
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.util.FloatProperty;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.AnimationConfig;
+import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PropertySetter;
+
+/**
+ * State handler for recents view. Manages UI changes and animations for recents view based off the
+ * current {@link LauncherState}.
+ *
+ * @param <T> the recents view
+ */
+public abstract class BaseRecentsViewStateController<T extends View>
+ implements StateHandler {
+ protected final T mRecentsView;
+ protected final Launcher mLauncher;
+
+ public BaseRecentsViewStateController(@NonNull Launcher launcher) {
+ mLauncher = launcher;
+ mRecentsView = launcher.getOverviewPanel();
+ }
+
+ @Override
+ public void setState(@NonNull LauncherState state) {
+ float[] scaleTranslationYFactor = state.getOverviewScaleAndTranslationYFactor(mLauncher);
+ SCALE_PROPERTY.set(mRecentsView, scaleTranslationYFactor[0]);
+ getTranslationYFactorProperty().set(mRecentsView, scaleTranslationYFactor[1]);
+ getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
+ }
+
+ @Override
+ public final void setStateWithAnimation(@NonNull final LauncherState toState,
+ @NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) {
+ if (!config.playAtomicComponent()) {
+ // The entire recents animation is played atomically.
+ return;
+ }
+ setStateWithAnimationInternal(toState, builder, config);
+ }
+
+ /**
+ * Core logic for animating the recents view UI.
+ *
+ * @param toState state to animate to
+ * @param builder animator set builder
+ * @param config current animation config
+ */
+ void setStateWithAnimationInternal(@NonNull final LauncherState toState,
+ @NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) {
+ PropertySetter setter = config.getPropertySetter(builder);
+ float[] scaleTranslationYFactor = toState.getOverviewScaleAndTranslationYFactor(mLauncher);
+ Interpolator scaleAndTransYInterpolator = getScaleAndTransYInterpolator(toState, builder);
+ setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleTranslationYFactor[0],
+ scaleAndTransYInterpolator);
+ setter.setFloat(mRecentsView, getTranslationYFactorProperty(), scaleTranslationYFactor[1],
+ scaleAndTransYInterpolator);
+ setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
+ builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
+ }
+
+ /**
+ * Get the interpolator to use for the scale and translation Y animation for the view.
+ *
+ * @param toState state to animate to
+ * @param builder animator set builder
+ * @return interpolator for scale and trans Y recents view animation
+ */
+ Interpolator getScaleAndTransYInterpolator(@NonNull final LauncherState toState,
+ @NonNull AnimatorSetBuilder builder) {
+ return builder.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR);
+ }
+
+ /**
+ * Get property for translation Y factor for the recents view.
+ *
+ * @return the float property for the recents view
+ */
+ abstract FloatProperty getTranslationYFactorProperty();
+
+ /**
+ * Get property for content alpha for the recents view.
+ *
+ * @return the float property for the view's content alpha
+ */
+ abstract FloatProperty getContentAlphaProperty();
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index 8684c58..ea0e552 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -47,8 +47,6 @@
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
/**
* Touch controller for handling various state transitions in portrait UI.
@@ -67,14 +65,16 @@
*/
private static final float RECENTS_FADE_THRESHOLD = 0.88f;
+ private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper;
+
private InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
// If true, we will finish the current animation instantly on second touch.
private boolean mFinishFastOnSecondTouch;
-
public PortraitStatesTouchController(Launcher l) {
super(l, SwipeDetector.VERTICAL);
+ mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l);
}
@Override
@@ -98,22 +98,18 @@
}
return false;
}
- RecentsView recentsView = mLauncher.getOverviewPanel();
if (mLauncher.isInState(ALL_APPS)) {
// In all-apps only listen if the container cannot scroll itself
if (!mLauncher.getAppsView().shouldContainerScroll(ev)) {
return false;
}
- } else if (mLauncher.isInState(OVERVIEW) && recentsView.getChildCount() > 0) {
- // Allow swiping up in the gap between the hotseat and overview.
- if (ev.getY() < recentsView.getChildAt(0).getBottom()) {
+ } else if (mLauncher.isInState(OVERVIEW)) {
+ if (!mOverviewPortraitStateTouchHelper.canInterceptTouch(ev)) {
return false;
}
} else {
// For all other states, only listen if the event originated below the hotseat height
- DeviceProfile dp = mLauncher.getDeviceProfile();
- int hotseatHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
- if (ev.getY() < (mLauncher.getDragLayer().getHeight() - hotseatHeight)) {
+ if (!isTouchOverHotseat(mLauncher, ev)) {
return false;
}
}
@@ -197,13 +193,12 @@
cancelPendingAnim();
- RecentsView recentsView = mLauncher.getOverviewPanel();
- TaskView taskView = recentsView.getTaskViewAt(recentsView.getNextPage());
- if (recentsView.shouldSwipeDownLaunchApp() && mFromState == OVERVIEW && mToState == NORMAL
- && taskView != null) {
+ if (mFromState == OVERVIEW && mToState == NORMAL
+ && mOverviewPortraitStateTouchHelper.shouldSwipeDownReturnToApp()) {
// Reset the state manager, when changing the interaction mode
mLauncher.getStateManager().goToState(OVERVIEW, false /* animate */);
- mPendingAnimation = recentsView.createTaskLauncherAnimation(taskView, maxAccuracy);
+ mPendingAnimation = mOverviewPortraitStateTouchHelper
+ .createSwipeDownToTaskAppAnimation(maxAccuracy);
mPendingAnimation.anim.setInterpolator(Interpolators.LINEAR);
Runnable onCancelRunnable = () -> {
@@ -213,7 +208,8 @@
mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation.anim, maxAccuracy,
onCancelRunnable);
mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
- totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher.getDeviceProfile());
+ totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher,
+ mLauncher.getDeviceProfile());
} else {
mCurrentAnimation = mLauncher.getStateManager()
.createAnimationToNewWorkspace(mToState, builder, maxAccuracy, this::clearState,
@@ -268,6 +264,19 @@
}
}
+ /**
+ * Whether the motion event is over the hotseat.
+ *
+ * @param launcher the launcher activity
+ * @param ev the event to check
+ * @return true if the event is over the hotseat
+ */
+ static boolean isTouchOverHotseat(Launcher launcher, MotionEvent ev) {
+ DeviceProfile dp = launcher.getDeviceProfile();
+ int hotseatHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
+ return (ev.getY() >= (launcher.getDragLayer().getHeight() - hotseatHeight));
+ }
+
private static class InterpolatorWrapper implements Interpolator {
public TimeInterpolator baseInterpolator = LINEAR;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
deleted file mode 100644
index abd2846..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ /dev/null
@@ -1,104 +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.uioverrides;
-
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
-import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR;
-import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
-import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
-import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
-
-import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationConfig;
-import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.PropertySetter;
-import com.android.quickstep.views.LauncherRecentsView;
-
-@TargetApi(Build.VERSION_CODES.O)
-public class RecentsViewStateController implements StateHandler {
-
- private final Launcher mLauncher;
- private final LauncherRecentsView mRecentsView;
-
- public RecentsViewStateController(Launcher launcher) {
- mLauncher = launcher;
- mRecentsView = launcher.getOverviewPanel();
- }
-
- @Override
- public void setState(LauncherState state) {
- mRecentsView.setContentAlpha(state.overviewUi ? 1 : 0);
- float[] scaleTranslationYFactor = state.getOverviewScaleAndTranslationYFactor(mLauncher);
- SCALE_PROPERTY.set(mRecentsView, scaleTranslationYFactor[0]);
- mRecentsView.setTranslationYFactor(scaleTranslationYFactor[1]);
- if (state.overviewUi) {
- mRecentsView.updateEmptyMessage();
- mRecentsView.resetTaskVisuals();
- }
- }
-
- @Override
- public void setStateWithAnimation(final LauncherState toState,
- AnimatorSetBuilder builder, AnimationConfig config) {
- if (!config.playAtomicComponent()) {
- // The entire recents animation is played atomically.
- return;
- }
- PropertySetter setter = config.getPropertySetter(builder);
- float[] scaleTranslationYFactor = toState.getOverviewScaleAndTranslationYFactor(mLauncher);
- Interpolator scaleAndTransYInterpolator = builder.getInterpolator(
- ANIM_OVERVIEW_SCALE, LINEAR);
- if (mLauncher.getStateManager().getState() == OVERVIEW && toState == FAST_OVERVIEW) {
- scaleAndTransYInterpolator = Interpolators.clampToProgress(
- QUICK_SCRUB_START_INTERPOLATOR, 0, QUICK_SCRUB_TRANSLATION_Y_FACTOR);
- }
- setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleTranslationYFactor[0],
- scaleAndTransYInterpolator);
- setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1],
- scaleAndTransYInterpolator);
- setter.setFloat(mRecentsView, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
- builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
-
- if (!toState.overviewUi) {
- builder.addOnFinishRunnable(mRecentsView::resetTaskVisuals);
- }
-
- if (toState.overviewUi) {
- ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1);
- updateAnim.addUpdateListener(valueAnimator -> {
- // While animating into recents, update the visible task data as needed
- mRecentsView.loadVisibleTaskData();
- });
- updateAnim.setDuration(config.duration);
- builder.play(updateAnim);
- mRecentsView.updateEmptyMessage();
- }
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
index 753f73a..50af4a1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -49,7 +50,7 @@
private static final String TAG = "OverviewSwipeController";
// Progress after which the transition is assumed to be a success in case user does not fling
- private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
+ public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
protected final T mActivity;
private final SwipeDetector mDetector;
@@ -231,6 +232,12 @@
mFlingBlockCheck.onEvent();
}
mCurrentAnimation.setPlayFraction(totalDisplacement * mProgressMultiplier);
+
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (mRecentsView.getCurrentPage() != 0 || isGoingUp) {
+ mRecentsView.redrawLiveTile(true);
+ }
+ }
return true;
}
@@ -267,6 +274,13 @@
anim.setFloatValues(nextFrameProgress, goingToEnd ? 1f : 0f);
anim.setDuration(animationDuration);
anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ anim.addUpdateListener(valueAnimator -> {
+ if (mRecentsView.getCurrentPage() != 0 || mCurrentAnimationIsGoingUp) {
+ mRecentsView.redrawLiveTile(true);
+ }
+ });
+ }
anim.start();
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 4e79fed..ff9d601 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,10 +16,8 @@
package com.android.launcher3.uioverrides;
-import static android.view.View.VISIBLE;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -36,18 +34,18 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppTransitionManagerImpl;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.QuickstepAppTransitionManagerImpl;
import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.TouchController;
+import com.android.launcher3.util.UiThreadHelper;
+import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
-import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.ActivityCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -58,6 +56,9 @@
public class UiFactory {
+ private static final AsyncCommand SET_SHELF_HEIGHT_CMD = (visible, height) ->
+ WindowManagerWrapper.getInstance().setShelfHeight(visible != 0, height);
+
public static TouchController[] createTouchControllers(Launcher launcher) {
boolean swipeUpEnabled = OverviewInteractionState.INSTANCE.get(launcher)
.isSwipeUpGestureEnabled();
@@ -78,7 +79,11 @@
&& !launcher.getDeviceProfile().isVerticalBarLayout()) {
list.add(new StatusBarTouchController(launcher));
}
- list.add(new LauncherTaskViewController(launcher));
+ TouchController taskSwipeController =
+ RecentsUiFactory.createTaskSwipeController(launcher);
+ if (taskSwipeController != null) {
+ list.add(taskSwipeController);
+ }
return list.toArray(new TouchController[list.size()]);
}
@@ -88,7 +93,8 @@
public static StateHandler[] getStateHandler(Launcher launcher) {
return new StateHandler[] {launcher.getAllAppsController(), launcher.getWorkspace(),
- new RecentsViewStateController(launcher), new BackButtonAlphaHandler(launcher)};
+ RecentsUiFactory.createRecentsViewStateController(launcher),
+ new BackButtonAlphaHandler(launcher)};
}
/**
@@ -108,8 +114,7 @@
}
public static void resetOverview(Launcher launcher) {
- RecentsView recents = launcher.getOverviewPanel();
- recents.reset();
+ RecentsUiFactory.resetRecents(launcher);
}
public static void onCreate(Launcher launcher) {
@@ -175,15 +180,13 @@
LauncherState state = launcher.getStateManager().getState();
if (!OverviewInteractionState.INSTANCE.get(launcher).swipeGestureInitializing()) {
DeviceProfile profile = launcher.getDeviceProfile();
- WindowManagerWrapper.getInstance().setShelfHeight(
- (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
- && !profile.isVerticalBarLayout(),
- profile.hotseatBarSizePx);
+ boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
+ && !profile.isVerticalBarLayout();
+ UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT_CMD,
+ visible ? 1 : 0, profile.hotseatBarSizePx);
}
- if (state == NORMAL) {
- launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
- }
+ RecentsUiFactory.onLauncherStateOrResumeChanged(launcher);
}
public static void onTrimMemory(Context context, int level) {
@@ -195,8 +198,8 @@
public static void useFadeOutAnimationForLauncherStart(Launcher launcher,
CancellationSignal cancellationSignal) {
- LauncherAppTransitionManagerImpl appTransitionManager =
- (LauncherAppTransitionManagerImpl) launcher.getAppTransitionManager();
+ QuickstepAppTransitionManagerImpl appTransitionManager =
+ (QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
appTransitionManager.setRemoteAnimationProvider((targets) -> {
// On the first call clear the reference.
@@ -237,26 +240,6 @@
}
public static void prepareToShowOverview(Launcher launcher) {
- RecentsView overview = launcher.getOverviewPanel();
- if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
- SCALE_PROPERTY.set(overview, 1.33f);
- }
- }
-
- private static class LauncherTaskViewController extends TaskViewTouchController<Launcher> {
-
- public LauncherTaskViewController(Launcher activity) {
- super(activity);
- }
-
- @Override
- protected boolean isRecentsInteractive() {
- return mActivity.isInState(OVERVIEW);
- }
-
- @Override
- protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
- mActivity.getStateManager().setCurrentUserControlledAnimation(animController);
- }
+ RecentsUiFactory.prepareToShowRecents(launcher);
}
}
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 4646fd7f..c3df9c7 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -21,7 +21,7 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
+import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS_SPRING;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
@@ -44,6 +44,8 @@
import android.os.Handler;
import android.os.Looper;
import android.view.View;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
@@ -53,9 +55,9 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.TestProtocol;
-import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.SpringObjectAnimator;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
@@ -77,9 +79,6 @@
import java.util.function.BiPredicate;
import java.util.function.Consumer;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-
/**
* Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
*/
@@ -150,11 +149,13 @@
*/
int getContainerType();
+ boolean isInLiveTileMode();
+
class LauncherActivityControllerHelper implements ActivityControlHelper<Launcher> {
@Override
public LayoutListener createLayoutListener(Launcher activity) {
- return new LauncherLayoutListener(activity);
+ return LauncherLayoutListener.resetAndGet(activity);
}
@Override
@@ -216,7 +217,7 @@
int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
return dp.hotseatBarSizePx + hotseatInset;
} else {
- return LayoutUtils.getShelfTrackingDistance(dp);
+ return LayoutUtils.getShelfTrackingDistance(context, dp);
}
}
@@ -295,8 +296,9 @@
AnimatorSet anim = new AnimatorSet();
if (!activity.getDeviceProfile().isVerticalBarLayout()) {
- AllAppsTransitionController controller = activity.getAllAppsController();
- ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(controller, ALL_APPS_PROGRESS,
+ Animator shiftAnim = new SpringObjectAnimator<>(activity.getAllAppsController(),
+ ALL_APPS_PROGRESS_SPRING, "allAppsSpringFromACH",
+ activity.getAllAppsController().getShiftRange(),
fromState.getVerticalProgress(activity),
endState.getVerticalProgress(activity));
shiftAnim.setInterpolator(LINEAR);
@@ -442,6 +444,13 @@
return launcher != null ? launcher.getStateManager().getState().containerType
: LauncherLogProto.ContainerType.APP;
}
+
+ @Override
+ public boolean isInLiveTileMode() {
+ Launcher launcher = getCreatedActivity();
+ return launcher != null && launcher.getStateManager().getState() == OVERVIEW &&
+ launcher.isStarted();
+ }
}
class FallbackActivityControllerHelper implements ActivityControlHelper<RecentsActivity> {
@@ -627,6 +636,11 @@
public int getContainerType() {
return LauncherLogProto.ContainerType.SIDELOADED_LAUNCHER;
}
+
+ @Override
+ public boolean isInLiveTileMode() {
+ return false;
+ }
}
interface LayoutListener {
diff --git a/quickstep/src/com/android/quickstep/LongSwipeHelper.java b/quickstep/src/com/android/quickstep/LongSwipeHelper.java
index 16214dd..80d37ae 100644
--- a/quickstep/src/com/android/quickstep/LongSwipeHelper.java
+++ b/quickstep/src/com/android/quickstep/LongSwipeHelper.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION;
import static com.android.quickstep.WindowTransformSwipeHandler.MIN_OVERSHOOT_DURATION;
@@ -41,7 +42,6 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.FlingBlockCheck;
import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
/**
* Utility class to handle long swipe from an app.
@@ -113,7 +113,7 @@
* MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
- if (blockedFling && !toAllApps) {
+ if (blockedFling && !toAllApps && !QUICKSTEP_SPRINGS.get()) {
Interpolators.OvershootParams overshoot = new OvershootParams(currentFraction,
currentFraction, endProgress, velocityPxPerMs, (int) mMaxSwipeDistance);
duration = (overshoot.duration + duration);
@@ -145,7 +145,12 @@
ValueAnimator animator = mAnimator.getAnimationPlayer();
animator.setDuration(duration).setInterpolator(interpolator);
animator.setFloatValues(currentFraction, endProgress);
- animator.start();
+
+ if (QUICKSTEP_SPRINGS.get()) {
+ mAnimator.dispatchOnStartWithVelocity(endProgress, velocityPxPerMs);
+ } else {
+ animator.start();
+ }
}
private void onSwipeAnimationComplete(boolean toAllApps, boolean isFling, Runnable callback) {
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index 95be188..cd71f3d 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -21,11 +21,9 @@
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.INVALID_POINTER_ID;
-
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
-import static com.android.systemui.shared.system.ActivityManagerWrapper
- .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import android.annotation.TargetApi;
@@ -74,6 +72,7 @@
private static final long LAUNCHER_DRAW_TIMEOUT_MS = 150;
public static final String DOWN_EVT = "OtherActivityTouchConsumer.DOWN";
+ private static final String UP_EVT = "OtherActivityTouchConsumer.UP";
private final SparseArray<RecentsAnimationState> mAnimationStates = new SparseArray<>();
private final RunningTaskInfo mRunningTask;
@@ -101,7 +100,7 @@
private VelocityTracker mVelocityTracker;
private MotionEventQueue mEventQueue;
- private boolean mIsGoingToHome;
+ private boolean mIsGoingToLauncher;
public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo,
RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl,
@@ -192,21 +191,31 @@
if (mPassedInitialSlop && mInteractionHandler != null) {
// Move
- mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
+ dispatchMotion(ev, displacement - mStartDisplacement);
}
break;
}
case ACTION_CANCEL:
// TODO: Should be different than ACTION_UP
case ACTION_UP: {
+ RaceConditionTracker.onEvent(UP_EVT, ENTER);
TraceHelper.endSection("TouchInt");
finishTouchTracking(ev);
+ RaceConditionTracker.onEvent(UP_EVT, EXIT);
break;
}
}
}
+ private void dispatchMotion(MotionEvent ev, float displacement) {
+ mInteractionHandler.updateDisplacement(displacement);
+ boolean isLandscape = isNavBarOnLeft() || isNavBarOnRight();
+ if (!isLandscape) {
+ mInteractionHandler.dispatchMotionEventToRecentsView(ev);
+ }
+ }
+
private void notifyGestureStarted() {
if (mInteractionHandler == null) {
return;
@@ -297,15 +306,16 @@
*/
private void finishTouchTracking(MotionEvent ev) {
if (mPassedInitialSlop && mInteractionHandler != null) {
- mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
+ dispatchMotion(ev, getDisplacement(ev) - mStartDisplacement);
mVelocityTracker.computeCurrentVelocity(1000,
ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
- float velocity = isNavBarOnRight() ? mVelocityTracker.getXVelocity(mActivePointerId)
- : isNavBarOnLeft() ? -mVelocityTracker.getXVelocity(mActivePointerId)
+ float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
+ float velocity = isNavBarOnRight() ? velocityX
+ : isNavBarOnLeft() ? -velocityX
: mVelocityTracker.getYVelocity(mActivePointerId);
- mInteractionHandler.onGestureEnded(velocity);
+ mInteractionHandler.onGestureEnded(velocity, velocityX);
} else {
// Since we start touch tracking on DOWN, we may reach this state without actually
// starting the gesture. In that case, just cleanup immediately.
@@ -326,7 +336,7 @@
if (mInteractionHandler != null) {
final WindowTransformSwipeHandler handler = mInteractionHandler;
mInteractionHandler = null;
- mIsGoingToHome = handler.mIsGoingToHome;
+ mIsGoingToLauncher = handler.mIsGoingToRecents;
mMainThreadExecutor.execute(handler::reset);
}
}
@@ -376,9 +386,9 @@
// Deferred gesture, start the animation and gesture tracking once we pass the actual
// touch slop
startTouchTrackingForWindowAnimation(ev.getEventTime());
- mPassedInitialSlop = true;
- mStartDisplacement = getDisplacement(ev);
}
+ mPassedInitialSlop = true;
+ mStartDisplacement = getDisplacement(ev);
notifyGestureStarted();
}
@@ -410,7 +420,7 @@
@Override
public boolean forceToLauncherConsumer() {
- return mIsGoingToHome;
+ return mIsGoingToLauncher;
}
@Override
@@ -421,6 +431,7 @@
private class RecentsAnimationState implements RecentsAnimationListener {
+ private static final String ANIMATION_START_EVT = "RecentsAnimationState.onAnimationStart";
private final int id;
private RecentsAnimationControllerCompat mController;
@@ -439,11 +450,13 @@
RecentsAnimationControllerCompat controller,
RemoteAnimationTargetCompat[] apps, Rect homeContentInsets,
Rect minimizedHomeBounds) {
+ RaceConditionTracker.onEvent(ANIMATION_START_EVT, ENTER);
mController = controller;
mTargets = new RemoteAnimationTargetSet(apps, MODE_CLOSING);
mHomeContentInsets = homeContentInsets;
mMinimizedHomeBounds = minimizedHomeBounds;
mEventQueue.onCommand(id);
+ RaceConditionTracker.onEvent(ANIMATION_START_EVT, EXIT);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index da5c4fa..db0150e 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASK_STABILIZER;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -124,6 +125,12 @@
mActivityControlHelper = controlHelper;
mTouchInteractionLog = touchInteractionLog;
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (mRecentsView.getRunningTaskView() != null) {
+ mRecentsView.getRunningTaskView().setShowScreenshot(false);
+ }
+ }
+
if (mIsQuickSwitch) {
mShouldSwitchToNext = true;
mPrevProgressDelta = 0;
@@ -342,6 +349,7 @@
if (action != null) {
action.run();
}
+ mRecentsView.setEnableDrawingLiveTile(true);
}
public void onTaskRemoved(int taskId) {
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index deedd32..89c7aba 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -18,9 +18,11 @@
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION;
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.STATUS_BAR_TRANSITION_PRE_DELAY;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl
+ .STATUS_BAR_TRANSITION_DURATION;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl
+ .STATUS_BAR_TRANSITION_PRE_DELAY;
import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
index 2f3cb5f..60bd9fb 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -97,11 +97,11 @@
}
/**
- * @param onFinishComplete A callback that runs after the animation controller has finished
- * on the background thread.
+ * @param onFinishComplete A callback that runs on the main thread after the animation
+ * controller has finished on the background thread.
*/
- public void finish(boolean toHome, Runnable onFinishComplete) {
- if (!toHome) {
+ public void finish(boolean toRecents, Runnable onFinishComplete) {
+ if (!toRecents) {
mExecutorService.submit(() -> finishBg(false, onFinishComplete));
return;
}
@@ -119,16 +119,17 @@
});
}
- protected void finishBg(boolean toHome, Runnable onFinishComplete) {
+ protected void finishBg(boolean toRecents, Runnable onFinishComplete) {
RecentsAnimationControllerCompat controller = mController;
mController = null;
- TraceHelper.endSection("RecentsController", "Finish " + controller + ", toHome=" + toHome);
+ TraceHelper.endSection("RecentsController", "Finish " + controller
+ + ", toRecents=" + toRecents);
if (controller != null) {
controller.setInputConsumerEnabled(false);
- controller.finish(toHome);
+ controller.finish(toRecents);
if (onFinishComplete != null) {
- onFinishComplete.run();
+ mMainThreadExecutor.execute(onFinishComplete);
}
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 21da566..e61c00a 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -68,7 +68,6 @@
private float mWindowCornerRadius = -1;
private Boolean mSupportsRoundedCornersOnWindows;
-
private RecentsModel(Context context) {
mContext = context;
diff --git a/quickstep/src/com/android/quickstep/TaskListStabilizer.java b/quickstep/src/com/android/quickstep/TaskListStabilizer.java
index 0d23973..3eb26b4 100644
--- a/quickstep/src/com/android/quickstep/TaskListStabilizer.java
+++ b/quickstep/src/com/android/quickstep/TaskListStabilizer.java
@@ -17,79 +17,153 @@
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASK_STABILIZER;
+import android.app.ActivityManager.RecentTaskInfo;
+import android.content.ComponentName;
+import android.os.Process;
import android.os.SystemClock;
-import android.util.SparseArray;
+import android.util.Log;
import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.IntSet;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
public class TaskListStabilizer {
private static final long TASK_CACHE_TIMEOUT_MS = 5000;
- private final SparseArray<Task> mTempMap = new SparseArray<>();
- private final IntArray mTempArray = new IntArray();
- private final IntSet mTempSet = new IntSet();
+ private static final int INVALID_TASK_ID = -1;
- private final IntArray mLastStableOrder = new IntArray();
- private final IntSet mLastSet = new IntSet();
- private final IntArray mLastUnstableOrder = new IntArray();
+ private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
- private long mLastReorderTime;
+ @Override
+ public void onTaskCreated(int taskId, ComponentName componentName) {
+ onTaskCreatedInternal(taskId);
+ }
- public ArrayList<Task> reorder(ArrayList<Task> tasks) {
+ @Override
+ public void onTaskMovedToFront(int taskId) {
+ onTaskMovedToFrontInternal(taskId);
+ }
+
+ @Override
+ public void onTaskRemoved(int taskId) {
+ onTaskRemovedInternal(taskId);
+ }
+ };
+
+ // Task ids ordered based on recency, 0th index is the latest task
+ private final IntArray mOrderedTaskIds;
+
+ // Wrapper objects used for sorting tasks
+ private final ArrayList<TaskWrapper> mTaskWrappers = new ArrayList<>();
+
+ // Information about recent task re-order which has not been applied yet
+ private int mScheduledMoveTaskId = INVALID_TASK_ID;
+ private long mScheduledMoveTime = 0;
+
+ public TaskListStabilizer() {
+ if (ENABLE_TASK_STABILIZER.get()) {
+ // Initialize the task ids map
+ List<RecentTaskInfo> rawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
+ Integer.MAX_VALUE, Process.myUserHandle().getIdentifier());
+ mOrderedTaskIds = new IntArray(rawTasks.size());
+ for (RecentTaskInfo info : rawTasks) {
+ mOrderedTaskIds.add(new TaskKey(info).id);
+ }
+
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+ } else {
+ mOrderedTaskIds = null;
+ }
+ }
+
+ private synchronized void onTaskCreatedInternal(int taskId) {
+ applyScheduledMoveUnchecked();
+ mOrderedTaskIds.add(taskId);
+ }
+
+ private synchronized void onTaskRemovedInternal(int taskId) {
+ applyScheduledMoveUnchecked();
+ mOrderedTaskIds.removeValue(taskId);
+ }
+
+ private void applyScheduledMoveUnchecked() {
+ if (mScheduledMoveTaskId != INVALID_TASK_ID) {
+ // Mode the scheduled task to front
+ mOrderedTaskIds.removeValue(mScheduledMoveTaskId);
+ mOrderedTaskIds.add(mScheduledMoveTaskId);
+ mScheduledMoveTaskId = INVALID_TASK_ID;
+ }
+ }
+
+ /**
+ * Checks if the scheduled move has timed out and moves the task to front accordingly.
+ */
+ private void applyScheduledMoveIfTime() {
+ if (mScheduledMoveTaskId != INVALID_TASK_ID
+ && (SystemClock.uptimeMillis() - mScheduledMoveTime) > TASK_CACHE_TIMEOUT_MS) {
+ applyScheduledMoveUnchecked();
+ }
+ }
+
+ private synchronized void onTaskMovedToFrontInternal(int taskId) {
+ applyScheduledMoveIfTime();
+ mScheduledMoveTaskId = taskId;
+ mScheduledMoveTime = SystemClock.uptimeMillis();
+ }
+
+
+ public synchronized ArrayList<Task> reorder(ArrayList<Task> tasks) {
if (!ENABLE_TASK_STABILIZER.get()) {
return tasks;
}
- // Create task id array
- int count = tasks.size();
- mTempArray.clear();
- mTempSet.clear();
- mTempMap.clear();
+ applyScheduledMoveIfTime();
- for (int i = 0; i < count; i++) {
- Task t = tasks.get(i);
- mTempMap.put(t.key.id, t);
-
- mTempSet.add(t.key.id);
- mTempArray.add(t.key.id);
+ // Ensure that we have enough wrappers
+ int taskCount = tasks.size();
+ for (int i = taskCount - mTaskWrappers.size(); i > 0; i--) {
+ mTaskWrappers.add(new TaskWrapper());
}
- if (mLastSet.equals(mTempSet) && isStabilizationQuickEnough()) {
- if (mLastStableOrder.equals(mTempArray)) {
- // Everything is same
- return tasks;
- }
+ List<TaskWrapper> listToSort = mTaskWrappers.size() == taskCount
+ ? mTaskWrappers : mTaskWrappers.subList(0, taskCount);
+ int missingTaskIndex = -taskCount;
- if (!mLastUnstableOrder.equals(mTempArray)) {
- // Fast reordering, record the current time.
- mLastUnstableOrder.copyFrom(mTempArray);
- mLastReorderTime = SystemClock.uptimeMillis();
- }
+ for (int i = 0; i < taskCount; i++){
+ TaskWrapper wrapper = listToSort.get(i);
+ wrapper.task = tasks.get(i);
+ wrapper.index = mOrderedTaskIds.indexOf(wrapper.task.key.id);
- // Reorder the tasks based on the last stable order.
- ArrayList<Task> sorted = new ArrayList<>(count);
- for (int i = 0; i < count; i++) {
- sorted.add(mTempMap.get(mLastStableOrder.get(i)));
+ // Ensure that missing tasks are put in the front, in the order they appear in the
+ // original list
+ if (wrapper.index < 0) {
+ wrapper.index = missingTaskIndex;
+ missingTaskIndex++;
}
- return sorted;
}
+ Collections.sort(listToSort);
- // Cache the data
- mLastStableOrder.copyFrom(mTempArray);
- mLastUnstableOrder.copyFrom(mTempArray);
- mLastSet.copyFrom(mTempSet);
-
- mLastReorderTime = SystemClock.uptimeMillis();
-
- return tasks;
+ ArrayList<Task> result = new ArrayList<>(taskCount);
+ for (int i = 0; i < taskCount; i++) {
+ result.add(listToSort.get(i).task);
+ }
+ return result;
}
- private boolean isStabilizationQuickEnough() {
- return (SystemClock.uptimeMillis() - mLastReorderTime) < TASK_CACHE_TIMEOUT_MS;
+ private static class TaskWrapper implements Comparable<TaskWrapper> {
+ Task task;
+ int index;
+
+ @Override
+ public int compareTo(TaskWrapper other) {
+ return Integer.compare(index, other.index);
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index cb214af..cc49d46 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -16,16 +16,13 @@
package com.android.quickstep;
-import android.content.Context;
import android.graphics.Matrix;
import android.view.View;
-import androidx.annotation.AnyThread;
-
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
-import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
@@ -34,11 +31,12 @@
import java.util.ArrayList;
import java.util.List;
+import androidx.annotation.AnyThread;
+
/**
* Factory class to create and add an overlays on the TaskView
*/
public class TaskOverlayFactory implements ResourceBasedOverride {
- private static TaskOverlayFactory sInstance;
/** Note that these will be shown in order from top to bottom, if available for the task. */
private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[]{
@@ -49,14 +47,9 @@
new TaskSystemShortcut.Freeform()
};
- public static TaskOverlayFactory get(Context context) {
- Preconditions.assertUIThread();
- if (sInstance == null) {
- sInstance = Overrides.getObject(TaskOverlayFactory.class,
- context.getApplicationContext(), R.string.task_overlay_factory_class);
- }
- return sInstance;
- }
+ public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
+ new MainThreadInitializedObject<>(c -> Overrides.getObject(TaskOverlayFactory.class,
+ c, R.string.task_overlay_factory_class));
@AnyThread
public boolean needAssist() {
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index a42ee09..8b6867f 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -22,6 +22,7 @@
import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT;
import static android.view.MotionEvent.ACTION_UP;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.ActivityManagerWrapper
.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
@@ -202,7 +203,7 @@
mEventQueue = new MotionEventQueue(mMainThreadChoreographer, TouchConsumer.NO_OP);
mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this);
mOverviewCallbacks = OverviewCallbacks.get(this);
- mTaskOverlayFactory = TaskOverlayFactory.get(this);
+ mTaskOverlayFactory = TaskOverlayFactory.INSTANCE.get(this);
mTouchInteractionLog = new TouchInteractionLog();
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
mInputConsumer.registerInputConsumer();
@@ -252,6 +253,11 @@
mOverviewCommandHelper.getActivityControlHelper().isResumed()) {
return OverviewTouchConsumer.newInstance(
mOverviewCommandHelper.getActivityControlHelper(), false, mTouchInteractionLog);
+ } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
+ mOverviewCommandHelper.getActivityControlHelper().isInLiveTileMode()) {
+ return OverviewTouchConsumer.newInstance(
+ mOverviewCommandHelper.getActivityControlHelper(), false, mTouchInteractionLog,
+ false /* waitForWindowAvailable */);
} else {
if (tracker == null) {
tracker = VelocityTracker.obtain();
@@ -298,9 +304,11 @@
private float mLastProgress = 0;
private boolean mStartPending = false;
private boolean mEndPending = false;
+ private boolean mWaitForWindowAvailable;
OverviewTouchConsumer(ActivityControlHelper<T> activityHelper, T activity,
- boolean startingInActivityBounds, TouchInteractionLog touchInteractionLog) {
+ boolean startingInActivityBounds, TouchInteractionLog touchInteractionLog,
+ boolean waitForWindowAvailable) {
mActivityHelper = activityHelper;
mActivity = activity;
mTarget = activity.getDragLayer();
@@ -311,6 +319,8 @@
.getQuickScrubController();
mTouchInteractionLog = touchInteractionLog;
mTouchInteractionLog.setTouchConsumer(this);
+
+ mWaitForWindowAvailable = waitForWindowAvailable;
}
@Override
@@ -433,7 +443,11 @@
}
};
- mActivityHelper.executeOnWindowAvailable(mActivity, action);
+ if (mWaitForWindowAvailable) {
+ mActivityHelper.executeOnWindowAvailable(mActivity, action);
+ } else {
+ action.run();
+ }
}
@Override
@@ -461,12 +475,19 @@
public static TouchConsumer newInstance(ActivityControlHelper activityHelper,
boolean startingInActivityBounds, TouchInteractionLog touchInteractionLog) {
+ return newInstance(activityHelper, startingInActivityBounds, touchInteractionLog,
+ true /* waitForWindowAvailable */);
+ }
+
+ public static TouchConsumer newInstance(ActivityControlHelper activityHelper,
+ boolean startingInActivityBounds, TouchInteractionLog touchInteractionLog,
+ boolean waitForWindowAvailable) {
BaseDraggingActivity activity = activityHelper.getCreatedActivity();
if (activity == null) {
return TouchConsumer.NO_OP;
}
return new OverviewTouchConsumer(activityHelper, activity, startingInActivityBounds,
- touchInteractionLog);
+ touchInteractionLog, waitForWindowAvailable);
}
}
}
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index e951750..33c7c4d 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -22,6 +22,10 @@
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.util.RaceConditionTracker.ENTER;
+import static com.android.launcher3.util.RaceConditionTracker.EXIT;
import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_FROM_APP_START_DURATION;
import static com.android.quickstep.QuickScrubController.QUICK_SWITCH_FROM_APP_START_DURATION;
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
@@ -46,10 +50,12 @@
import android.os.UserHandle;
import android.util.Log;
import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnDrawListener;
import android.view.WindowManager;
import android.view.animation.Interpolator;
+
import androidx.annotation.AnyThread;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
@@ -70,6 +76,7 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.util.RaceConditionTracker;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.ActivityControlHelper.AnimationFactory;
@@ -109,7 +116,7 @@
// Interaction finish states
private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 5;
- private static final int STATE_SCALED_CONTROLLER_APP = 1 << 6;
+ private static final int STATE_SCALED_CONTROLLER_LAST_TASK = 1 << 6;
private static final int STATE_HANDLER_INVALIDATED = 1 << 7;
private static final int STATE_GESTURE_STARTED_QUICKSTEP = 1 << 8;
@@ -127,7 +134,8 @@
private static final int STATE_SCREENSHOT_VIEW_SHOWN = 1 << 17;
private static final int STATE_RESUME_LAST_TASK = 1 << 18;
- private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 19;
+ private static final int STATE_START_NEW_TASK = 1 << 19;
+ private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 20;
private static final int LAUNCHER_UI_STATES =
@@ -153,7 +161,7 @@
"STATE_ACTIVITY_MULTIPLIER_COMPLETE",
"STATE_APP_CONTROLLER_RECEIVED",
"STATE_SCALED_CONTROLLER_RECENTS",
- "STATE_SCALED_CONTROLLER_APP",
+ "STATE_SCALED_CONTROLLER_LAST_TASK",
"STATE_HANDLER_INVALIDATED",
"STATE_GESTURE_STARTED_QUICKSTEP",
"STATE_GESTURE_STARTED_QUICKSCRUB",
@@ -166,6 +174,7 @@
"STATE_SCREENSHOT_CAPTURED",
"STATE_SCREENSHOT_VIEW_SHOWN",
"STATE_RESUME_LAST_TASK",
+ "STATE_START_NEW_TASK",
"STATE_ASSIST_DATA_RECEIVED",
};
@@ -173,15 +182,16 @@
public static final long MIN_SWIPE_DURATION = 80;
public static final long MIN_OVERSHOOT_DURATION = 120;
- public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f;
+ public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f;
private static final float SWIPE_DURATION_MULTIPLIER =
Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW));
+ private static final String SCREENSHOT_CAPTURED_EVT = "ScreenshotCaptured";
private final ClipAnimationHelper mClipAnimationHelper;
private final ClipAnimationHelper.TransformParams mTransformParams;
protected Runnable mGestureEndCallback;
- protected boolean mIsGoingToHome;
+ protected boolean mIsGoingToRecents;
private DeviceProfile mDp;
private int mTransitionDragLength;
@@ -190,6 +200,7 @@
// 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
// visible.
private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
+ private boolean mDispatchedDownEvent;
// To avoid UI jump when gesture is started, we offset the animation by the threshold.
private float mShiftAtGestureStart = 0;
@@ -298,10 +309,12 @@
mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_APP_CONTROLLER_RECEIVED,
this::sendRemoteAnimationsToAnimationFactory);
- mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_APP,
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_LAST_TASK,
this::resumeLastTaskForQuickstep);
mStateCallback.addCallback(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED,
this::resumeLastTask);
+ mStateCallback.addCallback(STATE_START_NEW_TASK | STATE_APP_CONTROLLER_RECEIVED,
+ this::startNewTask);
mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
| STATE_ACTIVITY_MULTIPLIER_COMPLETE
@@ -310,7 +323,7 @@
mStateCallback.addCallback(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
| STATE_SCALED_CONTROLLER_RECENTS,
- this::finishCurrentTransitionToHome);
+ this::finishCurrentTransitionToRecents);
mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
| STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_SCALED_CONTROLLER_RECENTS
@@ -327,7 +340,7 @@
mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
this::invalidateHandlerWithLauncher);
mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED
- | STATE_SCALED_CONTROLLER_APP,
+ | STATE_SCALED_CONTROLLER_LAST_TASK,
this::notifyTransitionCancelled);
mStateCallback.addCallback(QUICK_SCRUB_START_UI_STATE, this::onQuickScrubStartUi);
@@ -339,9 +352,11 @@
mStateCallback.addCallback(LONG_SWIPE_ENTER_STATE, this::checkLongSwipeCanEnter);
mStateCallback.addCallback(LONG_SWIPE_START_STATE, this::checkLongSwipeCanStart);
- mStateCallback.addChangeHandler(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
- | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
- (b) -> mRecentsView.setRunningTaskHidden(!b));
+ if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ mStateCallback.addChangeHandler(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
+ | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
+ (b) -> mRecentsView.setRunningTaskHidden(!b));
+ }
}
private void executeOnUiThread(Runnable action) {
@@ -410,6 +425,13 @@
SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, (applier) -> {
mSyncTransactionApplier = applier;
});
+ mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
+ if (!mBgLongSwipeMode) {
+ updateFinalShift();
+ }
+ });
+ mRecentsView.setRecentsAnimationWrapper(mRecentsAnimationWrapper);
+ mRecentsView.setClipAnimationHelper(mClipAnimationHelper);
mQuickScrubController = mRecentsView.getQuickScrubController();
mLayoutListener = mActivityControlHelper.createLayoutListener(mActivity);
@@ -462,6 +484,7 @@
}
private void setupRecentsViewUi() {
+ mRecentsView.setEnableDrawingLiveTile(false);
mRecentsView.showTask(mRunningTaskId);
mRecentsView.setRunningTaskHidden(true);
mRecentsView.setRunningTaskIconScaledDown(true);
@@ -535,15 +558,39 @@
} else {
offsetX = res.getDimensionPixelSize(R.dimen.recents_page_spacing)
+ tempRect.rect.width();
- float distanceToReachEdge = mDp.widthPx / 2 + tempRect.rect.width() / 2 +
- res.getDimensionPixelSize(R.dimen.recents_page_spacing);
- float interpolation = Math.min(1, offsetX / distanceToReachEdge);
- scale = TaskView.getCurveScaleForInterpolation(interpolation);
+ scale = getTaskCurveScaleForOffsetX(offsetX, tempRect.rect.width());
}
mClipAnimationHelper.offsetTarget(scale, Utilities.isRtl(res) ? -offsetX : offsetX, offsetY,
QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR);
}
+ private float getTaskCurveScaleForOffsetX(float offsetX, float taskWidth) {
+ float distanceToReachEdge = mDp.widthPx / 2 + taskWidth / 2 +
+ mContext.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
+ float interpolation = Math.min(1, offsetX / distanceToReachEdge);
+ return TaskView.getCurveScaleForInterpolation(interpolation);
+ }
+
+ @WorkerThread
+ public void dispatchMotionEventToRecentsView(MotionEvent event) {
+ if (mRecentsView == null) {
+ return;
+ }
+ // Pass the motion events to RecentsView to allow scrolling during swipe up.
+ if (mDispatchedDownEvent) {
+ mRecentsView.dispatchTouchEvent(event);
+ } else {
+ // The first event we dispatch should be ACTION_DOWN.
+ mDispatchedDownEvent = true;
+ MotionEvent downEvent = MotionEvent.obtain(event);
+ downEvent.setAction(MotionEvent.ACTION_DOWN);
+ int flags = downEvent.getEdgeFlags();
+ downEvent.setEdgeFlags(flags | TouchInteractionService.EDGE_NAV_BAR);
+ mRecentsView.dispatchTouchEvent(downEvent);
+ downEvent.recycle();
+ }
+ }
+
@WorkerThread
public void updateDisplacement(float displacement) {
// We are moving in the negative x/y direction
@@ -588,11 +635,21 @@
RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
if (controller != null) {
+ float offsetX = 0;
+ if (mRecentsView != null && mInteractionType == INTERACTION_NORMAL) {
+ int startScroll = mRecentsView.getScrollForPage(mRecentsView.indexOfChild(
+ mRecentsView.getRunningTaskView()));
+ offsetX = startScroll - mRecentsView.getScrollX();
+ offsetX *= mRecentsView.getScaleX();
+ }
+ float offsetScale = getTaskCurveScaleForOffsetX(offsetX,
+ mClipAnimationHelper.getTargetRect().width());
SyncRtSurfaceTransactionApplierCompat syncTransactionApplier
= Looper.myLooper() == mMainThreadHandler.getLooper()
? mSyncTransactionApplier
: null;
- mTransformParams.setProgress(shift).setSyncTransactionApplier(syncTransactionApplier);
+ mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale)
+ .setSyncTransactionApplier(syncTransactionApplier);
mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
mTransformParams);
@@ -607,10 +664,13 @@
}
private void updateFinalShiftUi() {
- if (mRecentsAnimationWrapper.getController() != null && mLayoutListener != null) {
- mLayoutListener.update(mCurrentShift.value > 1, mUiLongSwipeMode,
- mClipAnimationHelper.getCurrentRectWithInsets(),
- mClipAnimationHelper.getCurrentCornerRadius());
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (mRecentsAnimationWrapper.getController() != null && mLayoutListener != null) {
+ mLayoutListener.open();
+ mLayoutListener.update(mCurrentShift.value > 1, mUiLongSwipeMode,
+ mClipAnimationHelper.getCurrentRectWithInsets(),
+ mClipAnimationHelper.getCurrentCornerRadius());
+ }
}
final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
@@ -621,10 +681,17 @@
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
}
- // Update insets of the next previous task, as we might switch to it.
- TaskView nextTaskView = mRecentsView == null ? null : mRecentsView.getNextTaskView();
- if (mInteractionType == INTERACTION_NORMAL && nextTaskView != null) {
- nextTaskView.setFullscreenProgress(1 - mCurrentShift.value);
+ // Update insets of the adjacent tasks, as we might switch to them.
+ int runningTaskIndex = mRecentsView == null ? -1 : mRecentsView.getRunningTaskIndex();
+ if (mInteractionType == INTERACTION_NORMAL && runningTaskIndex >= 0) {
+ TaskView nextTaskView = mRecentsView.getTaskViewAt(runningTaskIndex + 1);
+ TaskView prevTaskView = mRecentsView.getTaskViewAt(runningTaskIndex - 1);
+ if (nextTaskView != null) {
+ nextTaskView.setFullscreenProgress(1 - mCurrentShift.value);
+ }
+ if (prevTaskView != null) {
+ prevTaskView.setFullscreenProgress(1 - mCurrentShift.value);
+ }
}
if (mLauncherTransitionController == null || mLauncherTransitionController
@@ -714,7 +781,7 @@
}
@WorkerThread
- public void onGestureEnded(float endVelocity) {
+ public void onGestureEnded(float endVelocity, float velocityX) {
float flingThreshold = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_threshold_velocity);
boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold;
@@ -723,9 +790,9 @@
mLogAction = isFling ? Touch.FLING : Touch.SWIPE;
if (mBgLongSwipeMode) {
- executeOnUiThread(() -> onLongSwipeGestureFinishUi(endVelocity, isFling));
+ executeOnUiThread(() -> onLongSwipeGestureFinishUi(endVelocity, isFling, velocityX));
} else {
- handleNormalGestureEnd(endVelocity, isFling);
+ handleNormalGestureEnd(endVelocity, isFling, velocityX);
}
}
@@ -735,38 +802,47 @@
if (mLauncherTransitionController != null) {
mLauncherTransitionController.getAnimationPlayer().end();
}
- // Hide the task view, if not already hidden
- setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
+ if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ // Hide the task view, if not already hidden
+ setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
+ }
return OverviewTouchConsumer.newInstance(mActivityControlHelper, true,
mTouchInteractionLog);
}
- private void handleNormalGestureEnd(float endVelocity, boolean isFling) {
+ private void handleNormalGestureEnd(float endVelocity, boolean isFling, float velocityX) {
float velocityPxPerMs = endVelocity / 1000;
+ float velocityXPxPerMs = velocityX / 1000;
long duration = MAX_SWIPE_DURATION;
float currentShift = mCurrentShift.value;
- final boolean goingToHome;
+ final boolean goingToRecents;
float endShift;
final float startShift;
Interpolator interpolator = DEACCEL;
+ final int nextPage = mRecentsView != null ? mRecentsView.getNextPage() : -1;
+ final int runningTaskIndex = mRecentsView != null ? mRecentsView.getRunningTaskIndex() : -1;
+ boolean goingToNewTask = mRecentsView != null && nextPage != runningTaskIndex;
+ final boolean reachedOverviewThreshold = currentShift >= MIN_PROGRESS_FOR_OVERVIEW;
if (!isFling) {
- goingToHome = currentShift >= MIN_PROGRESS_FOR_OVERVIEW && mGestureStarted;
- endShift = goingToHome ? 1 : 0;
+ goingToRecents = reachedOverviewThreshold && mGestureStarted;
+ endShift = goingToRecents ? 1 : 0;
long expectedDuration = Math.abs(Math.round((endShift - currentShift)
* MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
startShift = currentShift;
- interpolator = goingToHome ? OVERSHOOT_1_2 : DEACCEL;
+ interpolator = goingToRecents ? OVERSHOOT_1_2 : DEACCEL;
} else {
- goingToHome = endVelocity < 0;
- endShift = goingToHome ? 1 : 0;
+ // If user scrolled to a new task, only go to recents if they already passed
+ // the overview threshold. Otherwise, we'll snap to the new task and launch it.
+ goingToRecents = endVelocity < 0 && (!goingToNewTask || reachedOverviewThreshold);
+ endShift = goingToRecents ? 1 : 0;
startShift = Utilities.boundToRange(currentShift - velocityPxPerMs
* SINGLE_FRAME_MS / mTransitionDragLength, 0, 1);
float minFlingVelocity = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_min_velocity);
if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
- if (goingToHome) {
+ if (goingToRecents) {
Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams(
startShift, endShift, endShift, velocityPxPerMs, mTransitionDragLength);
endShift = overshoot.end;
@@ -784,11 +860,31 @@
}
}
}
- if (goingToHome) {
+ if (goingToRecents) {
mRecentsAnimationWrapper.enableTouchProxy();
+ } else if (goingToNewTask) {
+ // We aren't goingToRecents, and user scrolled/flung to a new task; snap to the closest
+ // task in that direction and launch it (in startNewTask()).
+ int taskToLaunch = runningTaskIndex + (nextPage > runningTaskIndex ? 1 : - 1);
+ if (taskToLaunch >= mRecentsView.getTaskViewCount()) {
+ // Scrolled to Clear all button, snap back to current task and resume it.
+ mRecentsView.snapToPage(runningTaskIndex, Math.toIntExact(duration));
+ goingToNewTask = false;
+ } else {
+ float distance = Math.abs(mRecentsView.getScrollForPage(taskToLaunch)
+ - mRecentsView.getScrollX());
+ int durationX = (int) Math.abs(distance / velocityXPxPerMs);
+ if (durationX > MAX_SWIPE_DURATION) {
+ durationX = Math.toIntExact(MAX_SWIPE_DURATION);
+ }
+ interpolator = Interpolators.scrollInterpolatorForVelocity(velocityXPxPerMs);
+ mRecentsView.snapToPage(taskToLaunch, durationX, interpolator);
+ duration = Math.max(duration, durationX);
+ }
}
- animateToProgress(startShift, endShift, duration, interpolator, goingToHome);
+ animateToProgress(startShift, endShift, duration, interpolator, goingToRecents,
+ goingToNewTask, velocityPxPerMs);
}
private void doLogGesture(boolean toLauncher) {
@@ -813,23 +909,28 @@
}
/** Animates to the given progress, where 0 is the current app and 1 is overview. */
- private void animateToProgress(float start, float end, long duration,
- Interpolator interpolator, boolean goingToHome) {
+ private void animateToProgress(float start, float end, long duration, Interpolator interpolator,
+ boolean goingToRecents, boolean goingToNewTask, float velocityPxPerMs) {
mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration,
- interpolator, goingToHome));
+ interpolator, goingToRecents, goingToNewTask, velocityPxPerMs));
}
private void animateToProgressInternal(float start, float end, long duration,
- Interpolator interpolator, boolean goingToHome) {
- mIsGoingToHome = goingToHome;
+ Interpolator interpolator, boolean goingToRecents, boolean goingToNewTask,
+ float velocityPxPerMs) {
+ mIsGoingToRecents = goingToRecents;
ObjectAnimator anim = mCurrentShift.animateToValue(start, end).setDuration(duration);
anim.setInterpolator(interpolator);
anim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
- setStateOnUiThread(mIsGoingToHome
- ? (STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
- | STATE_SCREENSHOT_VIEW_SHOWN) : STATE_SCALED_CONTROLLER_APP);
+ int recentsState = STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
+ | STATE_SCREENSHOT_VIEW_SHOWN;
+ setStateOnUiThread(mIsGoingToRecents
+ ? recentsState
+ : goingToNewTask
+ ? STATE_START_NEW_TASK
+ : STATE_SCALED_CONTROLLER_LAST_TASK);
}
});
anim.start();
@@ -854,7 +955,12 @@
mLauncherTransitionController.dispatchSetInterpolator(Interpolators.mapToProgress(
interpolator, adjustedStart, end));
mLauncherTransitionController.getAnimationPlayer().setDuration(adjustedDuration);
- mLauncherTransitionController.getAnimationPlayer().start();
+
+ if (QUICKSTEP_SPRINGS.get()) {
+ mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs);
+ } else {
+ mLauncherTransitionController.getAnimationPlayer().start();
+ }
}
});
}
@@ -868,10 +974,22 @@
@UiThread
private void resumeLastTask() {
- mRecentsAnimationWrapper.finish(false /* toHome */, null);
+ mRecentsAnimationWrapper.finish(false /* toRecents */, null);
mTouchInteractionLog.finishRecentsAnimation(false);
}
+ @UiThread
+ private void startNewTask() {
+ // Launch the task user scrolled to (mRecentsView.getNextPage()).
+ mRecentsAnimationWrapper.finish(true /* toRecents */, () -> {
+ mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false,
+ result -> setStateOnUiThread(STATE_HANDLER_INVALIDATED),
+ mMainThreadHandler);
+ });
+ mTouchInteractionLog.finishRecentsAnimation(false);
+ doLogGesture(false /* toLauncher */);
+ }
+
public void reset() {
if (mInteractionType != INTERACTION_QUICK_SCRUB) {
// Only invalidate the handler if we are not quick scrubbing, otherwise, it will be
@@ -889,6 +1007,10 @@
mActivityInitListener.unregister();
mTaskSnapshot = null;
+
+ if (mRecentsView != null) {
+ mRecentsView.setOnScrollChangeListener(null);
+ }
}
private void invalidateHandlerWithLauncher() {
@@ -913,57 +1035,68 @@
}
public void layoutListenerClosed() {
+ mRecentsView.setRunningTaskHidden(false);
if (mWasLauncherAlreadyVisible && mLauncherTransitionController != null) {
mLauncherTransitionController.setPlayFraction(1);
}
- mRecentsView.setRunningTaskHidden(false);
+ mRecentsView.setEnableDrawingLiveTile(true);
}
private void switchToScreenshot() {
- boolean finishTransitionPosted = false;
- RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
- if (controller != null) {
- // Update the screenshot of the task
- if (mTaskSnapshot == null) {
- mTaskSnapshot = controller.screenshotTask(mRunningTaskId);
- }
- TaskView taskView = mRecentsView.updateThumbnail(mRunningTaskId, mTaskSnapshot);
- if (taskView != null) {
- // Defer finishing the animation until the next launcher frame with the
- // new thumbnail
- finishTransitionPosted = new WindowCallbacksCompat(taskView) {
-
- // The number of frames to defer until we actually finish the animation
- private int mDeferFrameCount = 2;
-
- @Override
- public void onPostDraw(Canvas canvas) {
- if (mDeferFrameCount > 0) {
- mDeferFrameCount--;
- // Workaround, detach and reattach to invalidate the root node for
- // another draw
- detach();
- attach();
- taskView.invalidate();
- return;
- }
-
- setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
- detach();
- }
- }.attach();
- }
- }
- if (!finishTransitionPosted) {
- // If we haven't posted a draw callback, set the state immediately.
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+ } else {
+ boolean finishTransitionPosted = false;
+ RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
+ if (controller != null) {
+ // Update the screenshot of the task
+ if (mTaskSnapshot == null) {
+ mTaskSnapshot = controller.screenshotTask(mRunningTaskId);
+ }
+ TaskView taskView = mRecentsView.updateThumbnail(mRunningTaskId, mTaskSnapshot);
+ if (taskView != null) {
+ // Defer finishing the animation until the next launcher frame with the
+ // new thumbnail
+ finishTransitionPosted = new WindowCallbacksCompat(taskView) {
+
+ // The number of frames to defer until we actually finish the animation
+ private int mDeferFrameCount = 2;
+
+ @Override
+ public void onPostDraw(Canvas canvas) {
+ if (mDeferFrameCount > 0) {
+ mDeferFrameCount--;
+ // Workaround, detach and reattach to invalidate the root node for
+ // another draw
+ detach();
+ attach();
+ taskView.invalidate();
+ return;
+ }
+
+ setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+ detach();
+ }
+ }.attach();
+ }
+ }
+ if (!finishTransitionPosted) {
+ // If we haven't posted a draw callback, set the state immediately.
+ RaceConditionTracker.onEvent(SCREENSHOT_CAPTURED_EVT, ENTER);
+ setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+ RaceConditionTracker.onEvent(SCREENSHOT_CAPTURED_EVT, EXIT);
+ }
}
}
- private void finishCurrentTransitionToHome() {
- synchronized (mRecentsAnimationWrapper) {
- mRecentsAnimationWrapper.finish(true /* toHome */,
- () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
+ private void finishCurrentTransitionToRecents() {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+ } else {
+ synchronized (mRecentsAnimationWrapper) {
+ mRecentsAnimationWrapper.finish(true /* toRecents */,
+ () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
+ }
}
mTouchInteractionLog.finishRecentsAnimation(true);
}
@@ -999,7 +1132,8 @@
long duration = FeatureFlags.QUICK_SWITCH.get()
? QUICK_SWITCH_FROM_APP_START_DURATION
: QUICK_SCRUB_FROM_APP_START_DURATION;
- animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, true /* goingToHome */);
+ animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, true /* goingToRecents */,
+ false /* goingToNewTask */, 1f);
}
private void onQuickScrubStartUi() {
@@ -1012,7 +1146,6 @@
mLauncherTransitionController.getAnimationPlayer().end();
mLauncherTransitionController = null;
}
- mLayoutListener.finish();
mActivityControlHelper.onQuickInteractionStart(mActivity, mRunningTaskInfo, false,
mTouchInteractionLog);
@@ -1025,6 +1158,7 @@
if (mQuickScrubBlocked) {
return;
}
+ mLayoutListener.finish();
mQuickScrubController.onFinishedTransitionToQuickScrub();
mRecentsView.animateUpRunningTaskIconScale();
@@ -1149,17 +1283,19 @@
mLongSwipeController = mActivityControlHelper.getLongSwipeController(
mActivity, mRunningTaskId);
onLongSwipeDisplacementUpdated();
- setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
+ if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
+ }
}
- private void onLongSwipeGestureFinishUi(float velocity, boolean isFling) {
+ private void onLongSwipeGestureFinishUi(float velocity, boolean isFling, float velocityX) {
if (!mUiLongSwipeMode || mLongSwipeController == null) {
mUiLongSwipeMode = false;
- handleNormalGestureEnd(velocity, isFling);
+ handleNormalGestureEnd(velocity, isFling, velocityX);
return;
}
mUiLongSwipeMode = false;
- finishCurrentTransitionToHome();
+ finishCurrentTransitionToRecents();
mLongSwipeController.end(velocity, isFling,
() -> setStateOnUiThread(STATE_HANDLER_INVALIDATED));
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
index 64fbe96..a82a2c5 100644
--- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -16,6 +16,7 @@
package com.android.quickstep.util;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -73,6 +74,9 @@
// if the aspect ratio of the target is smaller than the aspect ratio of the source rect. In
// app window coordinates.
private final RectF mSourceWindowClipInsets = new RectF();
+ // The insets to be used for clipping the app window. For live tile, we don't transform the clip
+ // relative to the target rect.
+ private final RectF mSourceWindowClipInsetsForLiveTile = new RectF();
// The bounds of launcher (not including insets) in device coordinates
public final Rect mHomeStackBounds = new Rect();
@@ -150,6 +154,7 @@
Math.max(scaledTargetRect.top, 0),
Math.max(mSourceStackBounds.width() - scaledTargetRect.right, 0),
Math.max(mSourceStackBounds.height() - scaledTargetRect.bottom, 0));
+ mSourceWindowClipInsetsForLiveTile.set(mSourceWindowClipInsets);
mSourceRect.set(scaledTargetRect);
}
@@ -158,55 +163,53 @@
}
public RectF applyTransform(RemoteAnimationTargetSet targetSet, TransformParams params) {
- RectF currentRect;
- mTmpRectF.set(mTargetRect);
- Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale);
- float offsetYProgress = mOffsetYInterpolator.getInterpolation(params.progress);
- float progress = mInterpolator.getInterpolation(params.progress);
- currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
+ if (params.currentRect == null) {
+ RectF currentRect;
+ mTmpRectF.set(mTargetRect);
+ Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale * params.offsetScale);
+ float offsetYProgress = mOffsetYInterpolator.getInterpolation(params.progress);
+ float progress = mInterpolator.getInterpolation(params.progress);
+ currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
+ currentRect.offset(params.offsetX, 0);
- synchronized (mTargetOffset) {
- // Stay lined up with the center of the target, since it moves for quick scrub.
- currentRect.offset(mTargetOffset.x * mOffsetScale * progress,
- mTargetOffset.y * offsetYProgress);
+ synchronized (mTargetOffset) {
+ // Stay lined up with the center of the target, since it moves for quick scrub.
+ currentRect.offset(mTargetOffset.x * mOffsetScale * progress,
+ mTargetOffset.y * offsetYProgress);
+ }
+
+ final RectF sourceWindowClipInsets = params.forLiveTile
+ ? mSourceWindowClipInsetsForLiveTile : mSourceWindowClipInsets;
+ mClipRectF.left = sourceWindowClipInsets.left * progress;
+ mClipRectF.top = sourceWindowClipInsets.top * progress;
+ mClipRectF.right =
+ mSourceStackBounds.width() - (sourceWindowClipInsets.right * progress);
+ mClipRectF.bottom =
+ mSourceStackBounds.height() - (sourceWindowClipInsets.bottom * progress);
+ params.setCurrentRectAndTargetAlpha(currentRect, 1);
}
- mClipRectF.left = mSourceWindowClipInsets.left * progress;
- mClipRectF.top = mSourceWindowClipInsets.top * progress;
- mClipRectF.right =
- mSourceStackBounds.width() - (mSourceWindowClipInsets.right * progress);
- mClipRectF.bottom =
- mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * progress);
-
SurfaceParams[] surfaceParams = new SurfaceParams[targetSet.unfilteredApps.length];
for (int i = 0; i < targetSet.unfilteredApps.length; i++) {
RemoteAnimationTargetCompat app = targetSet.unfilteredApps[i];
mTmpMatrix.setTranslate(app.position.x, app.position.y);
Rect crop = app.sourceContainerBounds;
float alpha = 1f;
- int layer;
+ int layer = RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers);
float cornerRadius = 0f;
- float scale = currentRect.width() / crop.width();
+ float scale = params.currentRect.width() / crop.width();
if (app.mode == targetSet.targetMode) {
if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
- mTmpMatrix.setRectToRect(mSourceRect, currentRect, ScaleToFit.FILL);
+ mTmpMatrix.setRectToRect(mSourceRect, params.currentRect, ScaleToFit.FILL);
mTmpMatrix.postTranslate(app.position.x, app.position.y);
mClipRectF.roundOut(crop);
if (mSupportsRoundedCornersOnWindows) {
- cornerRadius = Utilities.mapRange(progress, mWindowCornerRadius,
+ cornerRadius = Utilities.mapRange(params.progress, mWindowCornerRadius,
mTaskCornerRadius);
}
- mCurrentCornerRadius = cornerRadius;
}
-
- if (app.isNotInRecents
- || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
- alpha = 1 - progress;
- }
-
- alpha = mTaskAlphaCallback.apply(app, alpha);
- layer = RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers);
- } else {
+ alpha = mTaskAlphaCallback.apply(app, params.targetAlpha);
+ } else if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
crop = null;
layer = Integer.MAX_VALUE;
}
@@ -217,7 +220,7 @@
cornerRadius / scale);
}
applySurfaceParams(params.syncTransactionApplier, surfaceParams);
- return currentRect;
+ return params.currentRect;
}
public RectF getCurrentRectWithInsets() {
@@ -361,14 +364,48 @@
public static class TransformParams {
float progress;
+ float offsetX;
+ float offsetScale;
+ @Nullable RectF currentRect;
+ float targetAlpha;
+ boolean forLiveTile;
+
SyncRtSurfaceTransactionApplierCompat syncTransactionApplier;
public TransformParams() {
progress = 0;
+ offsetX = 0;
+ offsetScale = 1;
+ currentRect = null;
+ targetAlpha = 0;
+ forLiveTile = false;
}
public TransformParams setProgress(float progress) {
this.progress = progress;
+ this.currentRect = null;
+ return this;
+ }
+
+ public TransformParams setCurrentRectAndTargetAlpha(RectF currentRect, float targetAlpha) {
+ this.currentRect = currentRect;
+ this.targetAlpha = targetAlpha;
+ this.progress = 1;
+ return this;
+ }
+
+ public TransformParams setOffsetX(float offsetX) {
+ this.offsetX = offsetX;
+ return this;
+ }
+
+ public TransformParams setOffsetScale(float offsetScale) {
+ this.offsetScale = offsetScale;
+ return this;
+ }
+
+ public TransformParams setForLiveTile(boolean forLiveTile) {
+ this.forLiveTile = forLiveTile;
return this;
}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 6ca0dce..ed585c1 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -21,14 +21,15 @@
import android.content.res.Resources;
import android.graphics.Rect;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-
-import java.lang.annotation.Retention;
-
import androidx.annotation.AnyThread;
import androidx.annotation.IntDef;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
+
+import java.lang.annotation.Retention;
+
public class LayoutUtils {
private static final int MULTI_WINDOW_STRATEGY_HALF_SCREEN = 1;
@@ -112,7 +113,14 @@
Math.round(x + outWidth), Math.round(y + outHeight));
}
- public static int getShelfTrackingDistance(DeviceProfile dp) {
+ public static int getShelfTrackingDistance(Context context, DeviceProfile dp) {
+ if (FeatureFlags.SWIPE_HOME.get()) {
+ // Track the bottom of the window rather than the top of the shelf.
+ int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
+ int spaceBetweenShelfAndRecents = (int) context.getResources().getDimension(
+ R.dimen.task_card_vert_space);
+ return shelfHeight + spaceBetweenShelfAndRecents;
+ }
// Start from a third of bottom inset to provide some shelf overlap.
return dp.hotseatBarSizePx + dp.getInsets().bottom / 3 - dp.edgeMarginPx * 2;
}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java b/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java
index 6ee305f..10283bf 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java
@@ -53,6 +53,7 @@
private final RecentsView mParent;
private final View mIconView;
private final int[] mIconPos;
+ private final TaskView mTaskView;
private final TaskThumbnailView mThumbnailView;
@@ -65,6 +66,7 @@
public TaskViewDrawable(TaskView tv, RecentsView parent) {
mParent = parent;
+ mTaskView = tv;
mIconView = tv.getIconView();
mIconPos = new int[2];
mIconScale = mIconView.getScaleX();
@@ -139,4 +141,8 @@
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
+
+ public TaskView getTaskView() {
+ return mTaskView;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index a92295e..9ad750b 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -20,18 +20,32 @@
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.res.Resources;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
+import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.StringRes;
+
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.systemui.shared.recents.model.Task;
+import java.lang.reflect.Method;
+import java.time.Duration;
+import java.util.Locale;
+
public final class DigitalWellBeingToast extends LinearLayout {
public interface InitializeCallback {
void call(float saturation, String contentDescription);
@@ -40,42 +54,138 @@
private static final String TAG = DigitalWellBeingToast.class.getSimpleName();
private Task mTask;
+ private ImageView mImage;
+ private TextView mText;
public DigitalWellBeingToast(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutDirection(Utilities.isRtl(getResources()) ?
View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
setOnClickListener((view) -> openAppUsageSettings());
+ }
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mText = findViewById(R.id.digital_well_being_remaining_time);
+ mImage = findViewById(R.id.digital_well_being_hourglass);
}
public void initialize(Task task, InitializeCallback callback) {
mTask = task;
Utilities.THREAD_POOL_EXECUTOR.execute(() -> {
- final long appUsageLimitTimeMs = -1;
- final long appRemainingTimeMs = -1;
- final boolean isGroupLimit = true;
+ long appUsageLimitTimeMs = -1;
+ long appRemainingTimeMs = -1;
+
+ try {
+ final Method getAppUsageLimit = LauncherApps.class.getMethod(
+ "getAppUsageLimit",
+ String.class,
+ UserHandle.class);
+ final Object usageLimit = getAppUsageLimit.invoke(
+ getContext().getSystemService(LauncherApps.class),
+ task.getTopComponent().getPackageName(),
+ UserHandle.of(task.key.userId));
+
+ if (usageLimit != null) {
+ final Class appUsageLimitClass = usageLimit.getClass();
+ appUsageLimitTimeMs = (long) appUsageLimitClass.getMethod("getTotalUsageLimit").
+ invoke(usageLimit);
+ appRemainingTimeMs = (long) appUsageLimitClass.getMethod("getUsageRemaining").
+ invoke(usageLimit);
+ }
+ } catch (Exception e) {
+ // Do nothing
+ }
+
+ final long appUsageLimitTimeMsFinal = appUsageLimitTimeMs;
+ final long appRemainingTimeMsFinal = appRemainingTimeMs;
+
post(() -> {
- final TextView remainingTimeText = findViewById(R.id.remaining_time);
- if (appUsageLimitTimeMs < 0) {
+ if (appUsageLimitTimeMsFinal < 0) {
setVisibility(GONE);
} else {
setVisibility(VISIBLE);
- remainingTimeText.setText(getText(appRemainingTimeMs, isGroupLimit));
+ mText.setText(getText(appRemainingTimeMsFinal));
+ mImage.setImageResource(appRemainingTimeMsFinal > 0 ?
+ R.drawable.hourglass_top : R.drawable.hourglass_bottom);
}
callback.call(
- appUsageLimitTimeMs >= 0 && appRemainingTimeMs < 0 ? 0 : 1,
- getContentDescriptionForTask(task, appRemainingTimeMs, isGroupLimit));
+ appUsageLimitTimeMsFinal >= 0 && appRemainingTimeMsFinal <= 0 ? 0 : 1,
+ getContentDescriptionForTask(
+ task, appUsageLimitTimeMsFinal, appRemainingTimeMsFinal));
});
});
}
- public static String getText(long remainingTime, boolean isGroupLimit) {
- return remainingTime < 0 ?
- "Grayed" :
- "Remaining time:" + (remainingTime + 59999) / 60000
- + " min " + (isGroupLimit ? "for group" : "for the app");
+ private String getReadableDuration(
+ Duration duration,
+ FormatWidth formatWidthHourAndMinute,
+ @StringRes int durationLessThanOneMinuteStringId,
+ boolean forceFormatWidth) {
+ int hours = Math.toIntExact(duration.toHours());
+ int minutes = Math.toIntExact(duration.minusHours(hours).toMinutes());
+
+ // Apply formatWidthHourAndMinute if both the hour part and the minute part are non-zero.
+ if (hours > 0 && minutes > 0) {
+ return MeasureFormat.getInstance(Locale.getDefault(), formatWidthHourAndMinute)
+ .formatMeasures(
+ new Measure(hours, MeasureUnit.HOUR),
+ new Measure(minutes, MeasureUnit.MINUTE));
+ }
+
+ // Apply formatWidthHourOrMinute if only the hour part is non-zero (unless forced).
+ if (hours > 0) {
+ return MeasureFormat.getInstance(
+ Locale.getDefault(),
+ forceFormatWidth ? formatWidthHourAndMinute : FormatWidth.WIDE)
+ .formatMeasures(new Measure(hours, MeasureUnit.HOUR));
+ }
+
+ // Apply formatWidthHourOrMinute if only the minute part is non-zero (unless forced).
+ if (minutes > 0) {
+ return MeasureFormat.getInstance(
+ Locale.getDefault()
+ , forceFormatWidth ? formatWidthHourAndMinute : FormatWidth.WIDE)
+ .formatMeasures(new Measure(minutes, MeasureUnit.MINUTE));
+ }
+
+ // Use a specific string for usage less than one minute but non-zero.
+ if (duration.compareTo(Duration.ZERO) > 0) {
+ return getResources().getString(durationLessThanOneMinuteStringId);
+ }
+
+ // Otherwise, return 0-minute string.
+ return MeasureFormat.getInstance(
+ Locale.getDefault(), forceFormatWidth ? formatWidthHourAndMinute : FormatWidth.WIDE)
+ .formatMeasures(new Measure(0, MeasureUnit.MINUTE));
+ }
+
+ private String getReadableDuration(
+ Duration duration,
+ FormatWidth formatWidthHourAndMinute,
+ @StringRes int durationLessThanOneMinuteStringId) {
+ return getReadableDuration(
+ duration,
+ formatWidthHourAndMinute,
+ durationLessThanOneMinuteStringId,
+ /* forceFormatWidth= */ false);
+ }
+
+ private String getShorterReadableDuration(Duration duration) {
+ return getReadableDuration(
+ duration, FormatWidth.NARROW, R.string.shorter_duration_less_than_one_minute);
+ }
+
+ private String getText(long remainingTime) {
+ final Resources resources = getResources();
+ return (remainingTime <= 0) ?
+ resources.getString(R.string.app_in_grayscale) :
+ resources.getString(
+ R.string.time_left_for_app,
+ getShorterReadableDuration(Duration.ofMillis(remainingTime)));
}
public void openAppUsageSettings() {
@@ -98,12 +208,12 @@
}
private String getContentDescriptionForTask(
- Task task, long appRemainingTimeMs, boolean isGroupLimit) {
- return appRemainingTimeMs > 0 ?
+ Task task, long appUsageLimitTimeMs, long appRemainingTimeMs) {
+ return appUsageLimitTimeMs >= 0 ?
getResources().getString(
R.string.task_contents_description_with_remaining_time,
task.titleDescription,
- getText(appRemainingTimeMs, isGroupLimit)) :
+ getText(appRemainingTimeMs)) :
task.titleDescription;
}
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
index 8ec5361..a8205cd 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
@@ -16,6 +16,7 @@
package com.android.quickstep.views;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import android.graphics.Canvas;
@@ -39,24 +40,44 @@
public class LauncherLayoutListener extends AbstractFloatingView
implements Insettable, LayoutListener {
+ public static LauncherLayoutListener resetAndGet(Launcher launcher) {
+ LauncherRecentsView lrv = launcher.getOverviewPanel();
+ LauncherLayoutListener listener = lrv.mLauncherLayoutListener;
+ if (listener.isOpen()) {
+ listener.close(false);
+ }
+ listener.setHandler(null);
+ return listener;
+ }
+
private final Launcher mLauncher;
private final Paint mPaint = new Paint();
private WindowTransformSwipeHandler mHandler;
private RectF mCurrentRect;
private float mCornerRadius;
- public LauncherLayoutListener(Launcher launcher) {
+ private boolean mWillNotDraw;
+
+ /**
+ * package private
+ */
+ LauncherLayoutListener(Launcher launcher) {
super(launcher, null);
mLauncher = launcher;
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
setLayoutParams(new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+ mWillNotDraw = willNotDraw();
+ super.setWillNotDraw(false);
}
@Override
public void update(boolean shouldFinish, boolean isLongSwipe, RectF currentRect,
- float cornerRadius) {
- if (shouldFinish) {
- finish();
+ float cornerRadius) {
+ if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (shouldFinish) {
+ finish();
+ }
return;
}
@@ -68,6 +89,12 @@
}
@Override
+ public void setWillNotDraw(boolean willNotDraw) {
+ // Prevent super call as that causes additional relayout.
+ mWillNotDraw = willNotDraw;
+ }
+
+ @Override
public void setHandler(WindowTransformSwipeHandler handler) {
mHandler = handler;
}
@@ -124,6 +151,8 @@
@Override
protected void onDraw(Canvas canvas) {
- canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
+ if (!mWillNotDraw) {
+ canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 697bb4f..f8eced0 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -15,10 +15,12 @@
*/
package com.android.quickstep.views;
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
+import static com.android.launcher3.AbstractFloatingView.TYPE_QUICKSTEP_PREVIEW;
import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
@@ -32,6 +34,8 @@
import android.view.View;
import android.view.ViewDebug;
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
@@ -40,6 +44,7 @@
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
import com.android.quickstep.util.LayoutUtils;
/**
@@ -62,9 +67,16 @@
}
};
+ /**
+ * A ratio representing the view's relative placement within its padded space. For example, 0
+ * is top aligned and 0.5 is centered vertically.
+ */
@ViewDebug.ExportedProperty(category = "launcher")
private float mTranslationYFactor;
+ private final TransformParams mTransformParams = new TransformParams();
+ final LauncherLayoutListener mLauncherLayoutListener;
+
public LauncherRecentsView(Context context) {
this(context, null);
}
@@ -76,11 +88,17 @@
public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setContentAlpha(0);
+ mLauncherLayoutListener = new LauncherLayoutListener(BaseActivity.fromContext(context));
}
@Override
protected void startHome() {
- mActivity.getStateManager().goToState(NORMAL);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ takeScreenshotAndFinishRecentsAnimation(true,
+ () -> mActivity.getStateManager().goToState(NORMAL));
+ } else {
+ mActivity.getStateManager().goToState(NORMAL);
+ }
}
@Override
@@ -92,6 +110,9 @@
public void setTranslationYFactor(float translationFactor) {
mTranslationYFactor = translationFactor;
setTranslationY(computeTranslationYForFactor(mTranslationYFactor));
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ redrawLiveTile(false);
+ }
}
public float computeTranslationYForFactor(float translationYFactor) {
@@ -168,4 +189,45 @@
public boolean shouldUseMultiWindowTaskSizeStrategy() {
return mActivity.isInMultiWindowModeCompat();
}
+
+ @Override
+ public void scrollTo(int x, int y) {
+ super.scrollTo(x, y);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile) {
+ redrawLiveTile(true);
+ }
+ }
+
+ @Override
+ public void redrawLiveTile(boolean mightNeedToRefill) {
+ AbstractFloatingView layoutListener = AbstractFloatingView.getTopOpenViewWithType(
+ mActivity, TYPE_QUICKSTEP_PREVIEW);
+ if (layoutListener != null && layoutListener.isOpen()) {
+ return;
+ }
+ if (mRecentsAnimationWrapper == null || mClipAnimationHelper == null) {
+ return;
+ }
+ TaskView taskView = getRunningTaskView();
+ if (taskView != null) {
+ taskView.getThumbnail().getGlobalVisibleRect(mTempRect);
+ int offsetX = (int) (mTaskWidth * taskView.getScaleX() * getScaleX()
+ - mTempRect.width());
+ int offsetY = (int) (mTaskHeight * taskView.getScaleY() * getScaleY()
+ - mTempRect.height());
+ if (((mCurrentPage != 0) || mightNeedToRefill) && offsetX > 0) {
+ mTempRect.right += offsetX;
+ }
+ if (mightNeedToRefill && offsetY > 0) {
+ mTempRect.top -= offsetY;
+ }
+ mTempRectF.set(mTempRect);
+ mTransformParams.setCurrentRectAndTargetAlpha(mTempRectF, taskView.getAlpha())
+ .setSyncTransactionApplier(mSyncTransactionApplier);
+ if (mRecentsAnimationWrapper.targetSet != null) {
+ mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
+ mTransformParams);
+ }
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 34b5748..5cbae65 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -17,14 +17,17 @@
package com.android.quickstep.views;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
+import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.uioverrides.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
+import static com.android.quickstep.util.ClipAnimationHelper.TransformParams;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
-import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
-
+import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
@@ -40,6 +43,7 @@
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
@@ -60,11 +64,13 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ListView;
+
import androidx.annotation.Nullable;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -75,8 +81,10 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.ViewPool;
import com.android.quickstep.OverviewCallbacks;
import com.android.quickstep.QuickScrubController;
+import com.android.quickstep.RecentsAnimationWrapper;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
@@ -87,7 +95,10 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.BackgroundExecutor;
import com.android.systemui.shared.system.PackageManagerWrapper;
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.WindowCallbacksCompat;
import java.util.ArrayList;
import java.util.function.Consumer;
@@ -97,7 +108,8 @@
*/
@TargetApi(Build.VERSION_CODES.P)
public abstract class RecentsView<T extends BaseActivity> extends PagedView implements Insettable,
- TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback {
+ TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
+ InvariantDeviceProfile.OnIDPChangeListener {
private static final String TAG = RecentsView.class.getSimpleName();
@@ -114,7 +126,14 @@
}
};
- private final Rect mTempRect = new Rect();
+ protected RecentsAnimationWrapper mRecentsAnimationWrapper;
+ protected ClipAnimationHelper mClipAnimationHelper;
+ protected SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier;
+ protected int mTaskWidth;
+ protected int mTaskHeight;
+ protected boolean mEnableDrawingLiveTile = false;
+ protected final Rect mTempRect = new Rect();
+ protected final RectF mTempRectF = new RectF();
private static final int DISMISS_TASK_DURATION = 300;
private static final int ADDITION_TASK_DURATION = 200;
@@ -136,6 +155,10 @@
// Keeps track of the previously known visible tasks for purposes of loading/unloading task data
private final SparseBooleanArray mHasVisibleTaskData = new SparseBooleanArray();
+ private final InvariantDeviceProfile mIdp;
+
+ private final ViewPool<TaskView> mTaskViewPool;
+
/**
* TODO: Call reloadIdNeeded in onTaskStackChanged.
*/
@@ -278,10 +301,15 @@
mActivity = (T) BaseActivity.fromContext(context);
mQuickScrubController = new QuickScrubController(mActivity, this);
mModel = RecentsModel.INSTANCE.get(context);
+ mIdp = InvariantDeviceProfile.INSTANCE.get(context);
+
mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
.inflate(R.layout.overview_clear_all_button, this, false);
mClearAllButton.setOnClickListener(this::dismissAllTasks);
+ mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
+ 10 /* initial size */);
+
mIsRtl = !Utilities.isRtl(getResources());
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
mTaskTopMargin = getResources()
@@ -320,12 +348,23 @@
}
@Override
+ public void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
+ if ((changeFlags & CHANGE_FLAG_ICON_PARAMS) == 0) {
+ return;
+ }
+ mModel.getIconCache().clear();
+ reset();
+ }
+
+ @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
updateTaskStackListenerState();
mModel.getThumbnailCache().getHighResLoadingState().addCallback(this);
mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+ mSyncTransactionApplier = new SyncRtSurfaceTransactionApplierCompat(this);
+ mIdp.addOnChangeListener(this);
}
@Override
@@ -335,6 +374,8 @@
mModel.getThumbnailCache().getHighResLoadingState().removeCallback(this);
mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+ mSyncTransactionApplier = null;
+ mIdp.removeOnChangeListener(this);
}
@Override
@@ -349,6 +390,7 @@
mHasVisibleTaskData.delete(task.key.id);
taskView.onTaskListVisibilityChanged(false /* visible */);
}
+ mTaskViewPool.recycle(taskView);
}
}
@@ -421,7 +463,8 @@
final boolean clearAllButtonDeadZoneConsumed =
mClearAllButton.getAlpha() == 1
&& mClearAllButtonDeadZoneRect.contains(x, y);
- if (!clearAllButtonDeadZoneConsumed
+ final boolean cameFromNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
+ if (!clearAllButtonDeadZoneConsumed && !cameFromNavBar
&& !mTaskViewDeadZoneRect.contains(x + getScrollX(), y)) {
mTouchDownToStartHome = true;
}
@@ -451,10 +494,6 @@
int oldChildCount = getChildCount();
- // Ensure there are as many views as there are tasks in the stack (adding and trimming as
- // necessary)
- final LayoutInflater inflater = LayoutInflater.from(getContext());
-
// Unload existing visible task data
unloadVisibleTaskData();
@@ -467,7 +506,7 @@
removeView(mClearAllButton);
}
for (int i = getChildCount(); i < requiredTaskCount; i++) {
- addView(inflater.inflate(R.layout.task, this, false));
+ addView(mTaskViewPool.getView());
}
while (getChildCount() > requiredTaskCount) {
removeView(getChildAt(getChildCount() - 1));
@@ -548,6 +587,8 @@
mInsets.set(insets);
DeviceProfile dp = mActivity.getDeviceProfile();
getTaskSize(dp, mTempRect);
+ mTaskWidth = mTempRect.width();
+ mTaskHeight = mTempRect.height();
// Keep this logic in sync with ActivityControlHelper.getTranslationYForQuickScrub.
mTempRect.top -= mTaskTopMargin;
@@ -681,11 +722,15 @@
protected abstract void startHome();
public void reset() {
+ setRunningTaskViewShowScreenshot(false);
mRunningTaskId = -1;
mRunningTaskTileHidden = false;
mIgnoreResetTaskId = -1;
mTaskListChangeId = -1;
+ mRecentsAnimationWrapper = null;
+ mClipAnimationHelper = null;
+
unloadVisibleTaskData();
setCurrentPage(0);
@@ -712,8 +757,7 @@
public void showTask(int runningTaskId) {
if (getChildCount() == 0) {
// Add an empty view for now until the task plan is loaded and applied
- final TaskView taskView = (TaskView) LayoutInflater.from(getContext())
- .inflate(R.layout.task, this, false);
+ final TaskView taskView = mTaskViewPool.getView();
addView(taskView);
addView(mClearAllButton);
@@ -732,6 +776,11 @@
return getTaskView(mRunningTaskId);
}
+ public int getRunningTaskIndex() {
+ TaskView tv = getRunningTaskView();
+ return tv == null ? -1 : indexOfChild(tv);
+ }
+
/**
* Hides the tile associated with {@link #mRunningTaskId}
*/
@@ -752,17 +801,27 @@
setRunningTaskIconScaledDown(false);
setRunningTaskHidden(false);
+ setRunningTaskViewShowScreenshot(true);
mRunningTaskId = runningTaskId;
+ setRunningTaskViewShowScreenshot(false);
setRunningTaskIconScaledDown(runningTaskIconScaledDown);
setRunningTaskHidden(runningTaskTileHidden);
- TaskView tv = getRunningTaskView();
- setCurrentPage(tv == null ? 0 : indexOfChild(tv));
+ setCurrentPage(getRunningTaskIndex());
// Load the tasks (if the loading is already
mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
}
+ private void setRunningTaskViewShowScreenshot(boolean showScreenshot) {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ TaskView runningTaskView = getRunningTaskView();
+ if (runningTaskView != null) {
+ runningTaskView.setShowScreenshot(showScreenshot);
+ }
+ }
+ }
+
public void showNextTask() {
TaskView runningTaskView = getRunningTaskView();
if (runningTaskView == null) {
@@ -970,26 +1029,40 @@
}
mPendingAnimation = pendingAnimation;
- mPendingAnimation.addEndListener((onEndListener) -> {
- if (onEndListener.isSuccess) {
- if (shouldRemoveTask) {
- removeTask(taskView.getTask(), draggedIndex, onEndListener, true);
- }
- int pageToSnapTo = mCurrentPage;
- if (draggedIndex < pageToSnapTo || pageToSnapTo == (getTaskViewCount() - 1)) {
- pageToSnapTo -= 1;
- }
- removeView(taskView);
+ mPendingAnimation.addEndListener(new Consumer<PendingAnimation.OnEndListener>() {
+ @Override
+ public void accept(PendingAnimation.OnEndListener onEndListener) {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
+ taskView.isRunningTask() && onEndListener.isSuccess) {
+ finishRecentsAnimation(true /* toHome */, () -> onEnd(onEndListener));
+ } else {
+ onEnd(onEndListener);
+ }
+ }
- if (getTaskViewCount() == 0) {
- removeView(mClearAllButton);
- startHome();
- } else {
- snapToPageImmediately(pageToSnapTo);
- }
- }
- resetTaskVisuals();
- mPendingAnimation = null;
+ private void onEnd(PendingAnimation.OnEndListener onEndListener) {
+ if (onEndListener.isSuccess) {
+ if (shouldRemoveTask) {
+ removeTask(taskView.getTask(), draggedIndex, onEndListener, true);
+ }
+
+ int pageToSnapTo = mCurrentPage;
+ if (draggedIndex < pageToSnapTo ||
+ pageToSnapTo == (getTaskViewCount() - 1)) {
+ pageToSnapTo -= 1;
+ }
+ removeView(taskView);
+
+ if (getTaskViewCount() == 0) {
+ removeView(mClearAllButton);
+ startHome();
+ } else {
+ snapToPageImmediately(pageToSnapTo);
+ }
+ }
+ resetTaskVisuals();
+ mPendingAnimation = null;
+ }
});
return pendingAnimation;
}
@@ -1337,20 +1410,38 @@
ObjectAnimator drawableAnim =
ObjectAnimator.ofFloat(drawable, TaskViewDrawable.PROGRESS, 1, 0);
drawableAnim.setInterpolator(LINEAR);
- drawableAnim.addUpdateListener((animator) -> {
- // Once we pass a certain threshold, update the sysui flags to match the target tasks'
- // flags
- mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW,
- animator.getAnimatedFraction() > UPDATE_SYSUI_FLAGS_THRESHOLD
- ? targetSysUiFlags
- : 0);
+ drawableAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ TransformParams mParams = new TransformParams();
- // Passing the threshold from taskview to fullscreen app will vibrate
- final boolean passed = animator.getAnimatedFraction() >= MIN_PROGRESS_FOR_OVERVIEW;
- if (passed != passedOverviewThreshold[0]) {
- passedOverviewThreshold[0] = passed;
- performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ // Once we pass a certain threshold, update the sysui flags to match the target
+ // tasks' flags
+ mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW,
+ animator.getAnimatedFraction() > UPDATE_SYSUI_FLAGS_THRESHOLD
+ ? targetSysUiFlags
+ : 0);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (mRecentsAnimationWrapper.targetSet != null
+ && drawable.getTaskView().isRunningTask()) {
+ mParams.setProgress(1 - animator.getAnimatedFraction())
+ .setSyncTransactionApplier(mSyncTransactionApplier)
+ .setForLiveTile(true);
+ drawable.getClipAnimationHelper().applyTransform(
+ mRecentsAnimationWrapper.targetSet, mParams);
+ } else {
+ redrawLiveTile(true);
+ }
+ }
+
+ // Passing the threshold from taskview to fullscreen app will vibrate
+ final boolean passed = animator.getAnimatedFraction() >=
+ SUCCESS_TRANSITION_PROGRESS;
+ if (passed != passedOverviewThreshold[0]) {
+ passedOverviewThreshold[0] = passed;
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ }
}
});
@@ -1449,4 +1540,74 @@
protected boolean isPageOrderFlipped() {
return true;
}
+
+ public void setEnableDrawingLiveTile(boolean enableDrawingLiveTile) {
+ mEnableDrawingLiveTile = enableDrawingLiveTile;
+ }
+
+ public void redrawLiveTile(boolean mightNeedToRefill) { }
+
+ public void setRecentsAnimationWrapper(RecentsAnimationWrapper recentsAnimationWrapper) {
+ mRecentsAnimationWrapper = recentsAnimationWrapper;
+ }
+
+ public void setClipAnimationHelper(ClipAnimationHelper clipAnimationHelper) {
+ mClipAnimationHelper = clipAnimationHelper;
+ }
+
+ public void finishRecentsAnimation(boolean toHome, Runnable onFinishComplete) {
+ if (mRecentsAnimationWrapper == null) {
+ if (onFinishComplete != null) {
+ onFinishComplete.run();
+ }
+ return;
+ }
+
+ mRecentsAnimationWrapper.finish(toHome, onFinishComplete);
+ }
+
+ public void takeScreenshotAndFinishRecentsAnimation(boolean toHome, Runnable onFinishComplete) {
+ if (mRecentsAnimationWrapper == null || getRunningTaskView() == null) {
+ if (onFinishComplete != null) {
+ onFinishComplete.run();
+ }
+ return;
+ }
+
+ RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
+ if (controller != null) {
+ // Update the screenshot of the task
+ ThumbnailData taskSnapshot = controller.screenshotTask(mRunningTaskId);
+ TaskView taskView = updateThumbnail(mRunningTaskId, taskSnapshot);
+ if (taskView != null) {
+ taskView.setShowScreenshot(true);
+ // Defer finishing the animation until the next launcher frame with the
+ // new thumbnail
+ new WindowCallbacksCompat(taskView) {
+
+ // The number of frames to defer until we actually finish the animation
+ private int mDeferFrameCount = 2;
+
+ @Override
+ public void onPostDraw(Canvas canvas) {
+ if (mDeferFrameCount > 0) {
+ mDeferFrameCount--;
+ // Workaround, detach and reattach to invalidate the root node for
+ // another draw
+ detach();
+ attach();
+ taskView.invalidate();
+ return;
+ }
+
+ detach();
+ mRecentsAnimationWrapper.finish(toHome, () -> {
+ onFinishComplete.run();
+ mRunningTaskId = -1;
+ });
+ }
+ }.attach();
+ }
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 667165b..bea646a 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -16,6 +16,7 @@
package com.android.quickstep.views;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
import android.animation.Animator;
@@ -206,7 +207,13 @@
R.layout.task_view_menu_option, this, false);
menuOption.setIconAndLabelFor(
menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text));
- menuOptionView.setOnClickListener(onClickListener);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ menuOptionView.setOnClickListener(
+ view -> mTaskView.getRecentsView().takeScreenshotAndFinishRecentsAnimation(true,
+ () -> onClickListener.onClick(view)));
+ } else {
+ menuOptionView.setOnClickListener(onClickListener);
+ }
mOptionLayout.addView(menuOptionView);
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index c2403a3..8169d73 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -16,6 +16,7 @@
package com.android.quickstep.views;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
import android.content.Context;
@@ -29,6 +30,8 @@
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Shader;
import android.util.AttributeSet;
@@ -76,6 +79,8 @@
private final boolean mIsDarkTextTheme;
private final Paint mPaint = new Paint();
private final Paint mBackgroundPaint = new Paint();
+ private final Paint mClearPaint = new Paint();
+ private final Paint mDimmingPaintAfterClearing = new Paint();
private final Matrix mMatrix = new Matrix();
@@ -102,15 +107,21 @@
public TaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mCornerRadius = getResources().getDimension(R.dimen.task_corner_radius);
- mOverlay = TaskOverlayFactory.get(context).createOverlay(this);
+ mOverlay = TaskOverlayFactory.INSTANCE.get(context).createOverlay(this);
mPaint.setFilterBitmap(true);
mBackgroundPaint.setColor(Color.WHITE);
+ mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+ mDimmingPaintAfterClearing.setColor(Color.BLACK);
mActivity = BaseActivity.fromContext(context);
mIsDarkTextTheme = Themes.getAttrBoolean(mActivity, R.attr.isWorkspaceDarkText);
}
- public void bind() {
+ public void bind(Task task) {
mOverlay.reset();
+ mTask = task;
+ int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000;
+ mPaint.setColor(color);
+ mBackgroundPaint.setColor(color);
}
/**
@@ -118,10 +129,6 @@
*/
public void setThumbnail(Task task, ThumbnailData thumbnailData) {
mTask = task;
- int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000;
- mPaint.setColor(color);
- mBackgroundPaint.setColor(color);
-
if (thumbnailData != null && thumbnailData.thumbnail != null) {
Bitmap bm = thumbnailData.thumbnail;
bm.prepareToDraw();
@@ -213,6 +220,15 @@
public void drawOnCanvas(Canvas canvas, float x, float y, float width, float height,
float cornerRadius) {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (mTask != null && getTaskView().isRunningTask() && !getTaskView().showScreenshot()) {
+ canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mClearPaint);
+ canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius,
+ mDimmingPaintAfterClearing);
+ return;
+ }
+ }
+
// Draw the background in all cases, except when the thumbnail data is opaque
final boolean drawBackgroundOnly = mTask == null || mTask.isLocked || mBitmapShader == null
|| mThumbnailData == null;
@@ -233,12 +249,17 @@
}
}
+ protected TaskView getTaskView() {
+ return (TaskView) getParent();
+ }
+
private void updateThumbnailPaintFilter() {
int mul = (int) ((1 - mDimAlpha * mDimAlphaMultiplier) * 255);
+ ColorFilter filter = getColorFilter(mul, mIsDarkTextTheme, mSaturation);
+ mBackgroundPaint.setColorFilter(filter);
+ mDimmingPaintAfterClearing.setAlpha(255 - mul);
if (mBitmapShader != null) {
- ColorFilter filter = getColorFilter(mul, mIsDarkTextTheme, mSaturation);
mPaint.setColorFilter(filter);
- mBackgroundPaint.setColorFilter(filter);
} else {
mPaint.setColorFilter(null);
mPaint.setColor(Color.argb(255, mul, mul, mul));
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 617ecf8..63c6805 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.BaseActivity.fromContext;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -45,9 +46,12 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.util.PendingAnimation;
+import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
@@ -66,7 +70,7 @@
/**
* A task in the Recents view.
*/
-public class TaskView extends FrameLayout implements PageCallbacks {
+public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private static final String TAG = TaskView.class.getSimpleName();
@@ -87,6 +91,7 @@
public static final long SCALE_ICON_DURATION = 120;
private static final long DIM_ANIM_DURATION = 700;
+ private static final long TASK_LAUNCH_ANIM_DURATION = 200;
public static final Property<TaskView, Float> ZOOM_SCALE =
new FloatProperty<TaskView>("zoomScale") {
@@ -118,7 +123,7 @@
new FloatProperty<TaskView>("focusTransition") {
@Override
public void setValue(TaskView taskView, float v) {
- taskView.setIconAndDimTransitionProgress(v);
+ taskView.setIconAndDimTransitionProgress(v, false /* invert */);
}
@Override
@@ -157,6 +162,8 @@
private Animator mIconAndDimAnimator;
private float mFocusTransitionProgress = 1;
+ private boolean mShowScreenshot;
+
// The current background requests to load the task thumbnail and icon
private TaskThumbnailCache.ThumbnailLoadRequest mThumbnailLoadRequest;
private TaskIconCache.IconLoadRequest mIconLoadRequest;
@@ -175,7 +182,15 @@
if (getTask() == null) {
return;
}
- launchTask(true /* animate */);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (isRunningTask()) {
+ createLaunchAnimationForRunningTask().start();
+ } else {
+ launchTask(true /* animate */);
+ }
+ } else {
+ launchTask(true /* animate */);
+ }
fromContext(context).getUserEventDispatcher().logTaskLaunchOrDismiss(
Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
@@ -199,7 +214,7 @@
*/
public void bind(Task task) {
mTask = task;
- mSnapshotView.bind();
+ mSnapshotView.bind(task);
}
public Task getTask() {
@@ -218,6 +233,19 @@
return mSnapshotView.getTaskOverlay();
}
+ public AnimatorPlaybackController createLaunchAnimationForRunningTask() {
+ final PendingAnimation pendingAnimation =
+ getRecentsView().createTaskLauncherAnimation(this, TASK_LAUNCH_ANIM_DURATION);
+ pendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
+ AnimatorPlaybackController currentAnimation = AnimatorPlaybackController
+ .wrap(pendingAnimation.anim, TASK_LAUNCH_ANIM_DURATION, null);
+ currentAnimation.setEndAction(() -> {
+ pendingAnimation.finish(true, Touch.SWIPE);
+ launchTask(false);
+ });
+ return currentAnimation;
+ }
+
public void launchTask(boolean animate) {
launchTask(animate, (result) -> {
if (!result) {
@@ -228,6 +256,21 @@
public void launchTask(boolean animate, Consumer<Boolean> resultCallback,
Handler resultCallbackHandler) {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (isRunningTask()) {
+ getRecentsView().finishRecentsAnimation(false,
+ () -> resultCallbackHandler.post(() -> resultCallback.accept(true)));
+ } else {
+ getRecentsView().takeScreenshotAndFinishRecentsAnimation(true,
+ () -> launchTaskInternal(animate, resultCallback, resultCallbackHandler));
+ }
+ } else {
+ launchTaskInternal(animate, resultCallback, resultCallbackHandler);
+ }
+ }
+
+ private void launchTaskInternal(boolean animate, Consumer<Boolean> resultCallback,
+ Handler resultCallbackHandler) {
if (mTask != null) {
final ActivityOptions opts;
if (animate) {
@@ -315,11 +358,17 @@
}
}
- private void setIconAndDimTransitionProgress(float progress) {
+ private void setIconAndDimTransitionProgress(float progress, boolean invert) {
+ if (invert) {
+ progress = 1 - progress;
+ }
mFocusTransitionProgress = progress;
mSnapshotView.setDimAlphaMultipler(progress);
- float scale = FAST_OUT_SLOW_IN.getInterpolation(Utilities.boundToRange(
- progress * DIM_ANIM_DURATION / SCALE_ICON_DURATION, 0, 1));
+ float iconScalePercentage = (float) SCALE_ICON_DURATION / DIM_ANIM_DURATION;
+ float lowerClamp = invert ? 1f - iconScalePercentage : 0;
+ float upperClamp = invert ? 1 : iconScalePercentage;
+ float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, lowerClamp, upperClamp)
+ .getInterpolation(progress);
mIconView.setScaleX(scale);
mIconView.setScaleY(scale);
}
@@ -340,19 +389,27 @@
}
protected void setIconScaleAndDim(float iconScale) {
+ setIconScaleAndDim(iconScale, false);
+ }
+
+ private void setIconScaleAndDim(float iconScale, boolean invert) {
if (mIconAndDimAnimator != null) {
mIconAndDimAnimator.cancel();
}
- setIconAndDimTransitionProgress(iconScale);
+ setIconAndDimTransitionProgress(iconScale, invert);
}
- public void resetVisualProperties() {
+ private void resetViewTransforms() {
setZoomScale(1);
setTranslationX(0f);
setTranslationY(0f);
setTranslationZ(0);
setAlpha(1f);
setIconScaleAndDim(1);
+ }
+
+ public void resetVisualProperties() {
+ resetViewTransforms();
if (!getRecentsView().getQuickScrubController().isQuickSwitch()) {
// Reset full screen progress unless we are doing back to back quick switch.
setFullscreenProgress(0);
@@ -360,6 +417,12 @@
}
@Override
+ public void onRecycle() {
+ resetViewTransforms();
+ setFullscreenProgress(0);
+ }
+
+ @Override
public void onPageScroll(ScrollState scrollState) {
float curveInterpolation =
CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
@@ -500,7 +563,7 @@
return super.performAccessibilityAction(action, arguments);
}
- private RecentsView getRecentsView() {
+ public RecentsView getRecentsView() {
return (RecentsView) getParent();
}
@@ -523,7 +586,8 @@
}
mFullscreenProgress = progress;
boolean isFullscreen = mFullscreenProgress > 0;
- mIconView.setVisibility(isFullscreen ? INVISIBLE : VISIBLE);
+ setIconScaleAndDim(progress, true /* invert */);
+ mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
setClipChildren(!isFullscreen);
setClipToPadding(!isFullscreen);
getThumbnail().invalidate();
@@ -532,4 +596,19 @@
public float getFullscreenProgress() {
return mFullscreenProgress;
}
+
+ public boolean isRunningTask() {
+ return this == getRecentsView().getRunningTaskView();
+ }
+
+ public void setShowScreenshot(boolean showScreenshot) {
+ mShowScreenshot = showScreenshot;
+ }
+
+ public boolean showScreenshot() {
+ if (!isRunningTask()) {
+ return true;
+ }
+ return mShowScreenshot;
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTests.java b/quickstep/tests/src/com/android/quickstep/TaplTests.java
index 6a1123e..347b7ac 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTests.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTests.java
@@ -104,7 +104,7 @@
clearLauncherData();
- mDevice.pressHome();
+ mLauncher.pressHome();
waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
waitForResumed("Launcher internal state is still Background");
}
@@ -424,7 +424,7 @@
executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets",
getWidgetsScroll(launcher) < flingForwardY));
- mDevice.pressHome();
+ mLauncher.pressHome();
waitForLauncherCondition("Widgets were not closed",
launcher -> getWidgetsView(launcher) == null);
}
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index ab62107..573da2d 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -40,11 +40,11 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Der blev ikke fundet nogen apps, som matcher \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Søg efter flere apps"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
- <string name="notifications_header" msgid="1404149926117359025">"Underretninger"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Notifikationer"</string>
<string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Hold en genvej nede for at samle den op."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Tryk to gange, og hold en genvej nede for at samle den op eller bruge tilpassede handlinger."</string>
<string name="out_of_space" msgid="4691004494942118364">"Der er ikke mere plads på denne startskærm."</string>
- <string name="hotseat_out_of_space" msgid="7448809638125333693">"Der er ikke mere plads i bakken Foretrukne"</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Der er ikke mere plads i bakken Favoritter"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Liste med apps"</string>
<string name="all_apps_button_personal_label" msgid="1315764287305224468">"Liste over personlige apps"</string>
<string name="all_apps_button_work_label" msgid="7270707118948892488">"Liste over apps til arbejdet"</string>
@@ -66,8 +66,8 @@
<string name="folder_hint_text" msgid="6617836969016293992">"Unavngiven mappe"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> er deaktiveret"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
- <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> underretning</item>
- <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> underretninger</item>
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifikation</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifikationer</item>
</plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Side %1$d ud af %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startskærm %1$d ud af %2$d"</string>
@@ -84,13 +84,13 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Deaktiveret af din administrator"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Tillad rotation af startskærmen"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Når telefonen roteres"</string>
- <string name="notification_dots_title" msgid="9062440428204120317">"Underretningscirkler"</string>
+ <string name="notification_dots_title" msgid="9062440428204120317">"Notifikationscirkler"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Til"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Fra"</string>
- <string name="title_missing_notification_access" msgid="7503287056163941064">"Kræver adgang til underretninger"</string>
- <string name="msg_missing_notification_access" msgid="281113995110910548">"Hvis du vil se underretningscirkler, skal du aktivere appunderretninger for <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="title_missing_notification_access" msgid="7503287056163941064">"Kræver adgang til notifikationer"</string>
+ <string name="msg_missing_notification_access" msgid="281113995110910548">"Hvis du vil se notifikationscirkler, skal du aktivere appnotifikationer for <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Skift indstillinger"</string>
- <string name="notification_dots_service_title" msgid="4284221181793592871">"Vis underretningscirkler"</string>
+ <string name="notification_dots_service_title" msgid="4284221181793592871">"Vis notifikationscirkler"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Føj ikon til startskærmen"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For nye apps"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Ukendt"</string>
@@ -126,16 +126,16 @@
<string name="action_decrease_height" msgid="282377193880900022">"Reducer højden"</string>
<string name="widget_resized" msgid="9130327887929620">"Størrelsen for widgetten er ændret til bredde <xliff:g id="NUMBER_0">%1$s</xliff:g> og højde <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
<string name="action_deep_shortcut" msgid="2864038805849372848">"Genveje"</string>
- <string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Genveje og underretninger"</string>
+ <string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Genveje og notifikationer"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Afvis"</string>
- <string name="notification_dismissed" msgid="6002233469409822874">"Underretningen blev afvist"</string>
+ <string name="notification_dismissed" msgid="6002233469409822874">"Notifikationen blev afvist"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personlige"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Arbejde"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbejdsprofil"</string>
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find arbejdsapps her"</string>
<string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Alle arbejdsapps har et badge og beskyttes af din organisation. Flyt apps til din startskærm, så du nemmere kan få adgang til dem."</string>
<string name="work_mode_on_label" msgid="4781128097185272916">"Administreret af din organisation"</string>
- <string name="work_mode_off_label" msgid="3194894777601421047">"Underretninger og apps er slået fra"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifikationer og apps er slået fra"</string>
<string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Luk"</string>
<string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Lukket"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mislykket: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 9d0ad45..d26661d 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -91,7 +91,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Um dir Benachrichtigungspunkte anzeigen zu lassen, aktiviere die Benachrichtigungen für die App \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
<string name="title_change_settings" msgid="1376365968844349552">"Einstellungen ändern"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"App-Benachrichtigungspunkte anzeigen"</string>
- <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Symbol zu Startbildschirm hinzufügen"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Symbol zum Startbildschirm hinzufügen"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Für neue Apps"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Unbekannt"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Entfernen"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index f93de69..253c4a2 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -39,7 +39,7 @@
<string name="all_apps_loading_message" msgid="5813968043155271636">"अॅप्स लोड करत आहे…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"अधिक अॅप्स शोधा"</string>
- <string name="label_application" msgid="8531721983832654978">"अॅप"</string>
+ <string name="label_application" msgid="8531721983832654978">"ॲप"</string>
<string name="notifications_header" msgid="1404149926117359025">"सूचना"</string>
<string name="long_press_shortcut_to_add" msgid="4524750017792716791">"शॉर्टकट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"शॉर्टकट निवडण्यासाठी किंवा कस्टम क्रिया वापरण्यासाठी दोनदा टॅप करा आणि धरून ठेवा."</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 8c4dd1e..53877ff 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -33,9 +33,11 @@
<attr name="workspaceKeyShadowColor" format="color" />
<attr name="workspaceStatusBarScrim" format="reference" />
<attr name="widgetsTheme" format="reference" />
- <attr name="folderDotColor" format="color" />
<attr name="loadingIconColor" format="color" />
+ <attr name="folderDotColor" format="color" />
+ <attr name="folderIconRadius" format="float" />
+
<!-- BubbleTextView specific attributes. -->
<declare-styleable name="BubbleTextView">
<attr name="layoutHorizontal" format="boolean" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a1089c6..51350c0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -336,6 +336,4 @@
<!-- Failed action error message: e.g. Failed: Pause -->
<string name="remote_action_failed">Failed: <xliff:g id="what" example="Pause">%1$s</xliff:g></string>
-
- <string name="task_contents_description_with_remaining_time" translatable="false"><xliff:g id="task_description" example="GMail">%1$s</xliff:g>, <xliff:g id="remaining_time" example="7 minutes">%2$s</xliff:g></string>
</resources>
diff --git a/res/xml/folder_shapes.xml b/res/xml/folder_shapes.xml
new file mode 100644
index 0000000..e60d333
--- /dev/null
+++ b/res/xml/folder_shapes.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shapes xmlns:launcher="http://schemas.android.com/apk/res-auto" >
+
+ <Circle launcher:folderIconRadius="1" />
+
+ <!-- Default icon for AOSP -->
+ <RoundedSquare launcher:folderIconRadius="0.16" />
+
+ <!-- Rounded icon from RRO -->
+ <RoundedSquare launcher:folderIconRadius="0.6" />
+
+ <!-- Square icon -->
+ <RoundedSquare launcher:folderIconRadius="0" />
+
+ <TearDrop launcher:folderIconRadius="0.3" />
+ <Squircle launcher:folderIconRadius="0.2" />
+
+</shapes>
\ No newline at end of file
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 1b953d4..5137774 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -226,7 +226,7 @@
/**
* Used to set the override visibility state, used only to handle the transition home with the
* recents animation.
- * @see LauncherAppTransitionManagerImpl#getWallpaperOpenRunner()
+ * @see QuickstepAppTransitionManagerImpl#getWallpaperOpenRunner()
*/
public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
mForceInvisible |= flag;
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 5c842a5..dafd5bb 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -30,12 +30,16 @@
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.SparseArray;
+import android.util.TypedValue;
import android.util.Xml;
import android.view.Display;
import android.view.WindowManager;
import com.android.launcher3.util.ConfigMonitor;
+import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.Themes;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -44,6 +48,7 @@
import java.util.ArrayList;
import java.util.Collections;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
public class InvariantDeviceProfile {
@@ -91,6 +96,8 @@
public int fillResIconDpi;
public float iconTextSize;
+ private SparseArray<TypedValue> mExtraAttrs;
+
/**
* Number of icons inside the hotseat area.
*/
@@ -122,6 +129,7 @@
numHotseatIcons = p.numHotseatIcons;
defaultLayoutId = p.defaultLayoutId;
demoModeLayoutId = p.demoModeLayoutId;
+ mExtraAttrs = p.mExtraAttrs;
}
@TargetApi(23)
@@ -131,6 +139,13 @@
APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
}
+ public InvariantDeviceProfile(Context context, String gridName) {
+ String newName = initGrid(context, gridName);
+ if (newName == null || !newName.equals(gridName)) {
+ throw new IllegalArgumentException("Unknown grid name");
+ }
+ }
+
/**
* Retrieve system defined or RRO overriden icon shape.
*/
@@ -142,7 +157,7 @@
return context.getResources().getString(CONFIG_ICON_MASK_RES_ID);
}
- private void initGrid(Context context, String gridName) {
+ private String initGrid(Context context, String gridName) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics dm = new DisplayMetrics();
@@ -171,6 +186,8 @@
demoModeLayoutId = closestProfile.demoModeLayoutId;
numFolderRows = closestProfile.numFolderRows;
numFolderColumns = closestProfile.numFolderColumns;
+ mExtraAttrs = closestProfile.extraAttrs;
+
if (!closestProfile.name.equals(gridName)) {
Utilities.getPrefs(context).edit()
.putString(KEY_IDP_GRID_NAME, closestProfile.name).apply();
@@ -208,19 +225,28 @@
} else {
defaultWallpaperSize = new Point(Math.max(smallSide * 2, largeSide), largeSide);
}
+ return closestProfile.name;
+ }
+
+ @Nullable
+ public TypedValue getAttrValue(int attr) {
+ return mExtraAttrs == null ? null : mExtraAttrs.get(attr);
}
public void addOnChangeListener(OnIDPChangeListener listener) {
mChangeListeners.add(listener);
}
+ public void removeOnChangeListener(OnIDPChangeListener listener) {
+ mChangeListeners.remove(listener);
+ }
+
private void killProcess(Context context) {
Log.e("ConfigMonitor", "restarting launcher");
android.os.Process.killProcess(android.os.Process.myPid());
}
public void verifyConfigChangedInBackground(final Context context) {
-
String savedIconMaskPath = getDevicePrefs(context).getString(KEY_ICON_PATH_REF, "");
// Good place to check if grid size changed in themepicker when launcher was dead.
if (savedIconMaskPath.isEmpty()) {
@@ -233,6 +259,12 @@
}
}
+ public void setCurrentGrid(Context context, String gridName) {
+ Context appContext = context.getApplicationContext();
+ Utilities.getPrefs(appContext).edit().putString(KEY_IDP_GRID_NAME, gridName).apply();
+ new MainThreadExecutor().execute(() -> onConfigChanged(appContext));
+ }
+
private void onConfigChanged(Context context) {
// Config changes, what shall we do?
InvariantDeviceProfile oldProfile = new InvariantDeviceProfile(this);
@@ -273,7 +305,8 @@
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
- if ((type == XmlPullParser.START_TAG) && "grid-option".equals(parser.getName())) {
+ if ((type == XmlPullParser.START_TAG)
+ && GridOption.TAG_NAME.equals(parser.getName())) {
GridOption gridOption = new GridOption(context, Xml.asAttributeSet(parser));
final int displayDepth = parser.getDepth();
@@ -422,11 +455,13 @@
}
- private static final class GridOption {
+ public static final class GridOption {
- private final String name;
- private final int numRows;
- private final int numColumns;
+ public static final String TAG_NAME = "grid-option";
+
+ public final String name;
+ public final int numRows;
+ public final int numColumns;
private final int numFolderRows;
private final int numFolderColumns;
@@ -436,7 +471,9 @@
private final int defaultLayoutId;
private final int demoModeLayoutId;
- GridOption(Context context, AttributeSet attrs) {
+ private final SparseArray<TypedValue> extraAttrs;
+
+ public GridOption(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.GridDisplayOption);
name = a.getString(R.styleable.GridDisplayOption_name);
@@ -454,6 +491,9 @@
numFolderColumns = a.getInt(
R.styleable.GridDisplayOption_numFolderColumns, numColumns);
a.recycle();
+
+ extraAttrs = Themes.createValueMap(context, attrs,
+ IntArray.wrap(R.styleable.GridDisplayOption));
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index f09b6e8..b8b1181 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -189,6 +189,8 @@
// Type: SparseArray<Parcelable>
private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel";
public static final String ON_CREATE_EVT = "Launcher.onCreate";
+ private static final String ON_START_EVT = "Launcher.onStart";
+ private static final String ON_RESUME_EVT = "Launcher.onResume";
private LauncherStateManager mStateManager;
@@ -762,12 +764,14 @@
@Override
protected void onStart() {
+ RaceConditionTracker.onEvent(ON_START_EVT, ENTER);
super.onStart();
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onStart();
}
mAppWidgetHost.setListenIfResumed(true);
NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
+ RaceConditionTracker.onEvent(ON_START_EVT, EXIT);
}
private void logOnDelayedResume() {
@@ -780,6 +784,7 @@
@Override
protected void onResume() {
+ RaceConditionTracker.onEvent(ON_RESUME_EVT, ENTER);
TraceHelper.beginSection("ON_RESUME");
super.onResume();
TraceHelper.partitionSection("ON_RESUME", "superCall");
@@ -802,6 +807,7 @@
UiFactory.onLauncherStateOrResumeChanged(this);
TraceHelper.endSection("ON_RESUME");
+ RaceConditionTracker.onEvent(ON_RESUME_EVT, EXIT);
}
@Override
@@ -1377,6 +1383,11 @@
}
private void setWorkspaceLoading(boolean value) {
+ if (com.android.launcher3.Utilities.IS_RUNNING_IN_TEST_HARNESS
+ && com.android.launcher3.Utilities.IS_DEBUG_DEVICE) {
+ android.util.Log.d("b/117332845", "setWorkspaceLoading " + value + " @ " +
+ android.util.Log.getStackTraceString(new Throwable()));
+ }
mWorkspaceLoading = value;
}
diff --git a/src/com/android/launcher3/MainProcessInitializer.java b/src/com/android/launcher3/MainProcessInitializer.java
index 9692d73..93df025 100644
--- a/src/com/android/launcher3/MainProcessInitializer.java
+++ b/src/com/android/launcher3/MainProcessInitializer.java
@@ -38,6 +38,6 @@
FileLog.setDir(context.getApplicationContext().getFilesDir());
FeatureFlags.initialize(context);
SessionCommitReceiver.applyDefaultUserPrefs(context);
- FolderShape.init();
+ FolderShape.init(context);
}
}
diff --git a/src/com/android/launcher3/ProgressInterface.java b/src/com/android/launcher3/ProgressInterface.java
new file mode 100644
index 0000000..663d8ba
--- /dev/null
+++ b/src/com/android/launcher3/ProgressInterface.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+/**
+ * Progress is defined as a value with range [0, 1], and is specific to each implementor.
+ * It is used when there is a transition from one state of the UI to another.
+ */
+public interface ProgressInterface {
+ void setProgress(float progress);
+ float getProgress();
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index ffbf34c..e8e93fe 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -1,7 +1,6 @@
package com.android.launcher3.allapps;
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER;
import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR;
@@ -15,9 +14,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
import android.util.Property;
-import android.view.View;
import android.view.animation.Interpolator;
import com.android.launcher3.DeviceProfile;
@@ -26,13 +23,17 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.ProgressInterface;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.SpringObjectAnimator;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+
/**
* Handles AllApps view transition.
* 1) Slides all apps view using direct manipulation
@@ -43,7 +44,8 @@
* If release velocity < THRES1, snap according to either top or bottom depending on whether it's
* closer to top or closer to the page indicator.
*/
-public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener {
+public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener,
+ ProgressInterface {
public static final Property<AllAppsTransitionController, Float> ALL_APPS_PROGRESS =
new Property<AllAppsTransitionController, Float>(Float.class, "allAppsProgress") {
@@ -59,6 +61,19 @@
}
};
+ public static final FloatPropertyCompat<AllAppsTransitionController> ALL_APPS_PROGRESS_SPRING
+ = new FloatPropertyCompat<AllAppsTransitionController>("allAppsProgressSpring") {
+ @Override
+ public float getValue(AllAppsTransitionController controller) {
+ return controller.mProgress;
+ }
+
+ @Override
+ public void setValue(AllAppsTransitionController controller, float progress) {
+ controller.setProgress(progress);
+ }
+ };
+
private AllAppsContainerView mAppsView;
private ScrimView mScrimView;
@@ -112,6 +127,7 @@
* @see #setState(LauncherState)
* @see #setStateWithAnimation(LauncherState, AnimatorSetBuilder, AnimationConfig)
*/
+ @Override
public void setProgress(float progress) {
mProgress = progress;
mScrimView.setProgress(progress);
@@ -136,6 +152,7 @@
}
}
+ @Override
public float getProgress() {
return mProgress;
}
@@ -174,8 +191,8 @@
Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW
? builder.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN)
: FAST_OUT_SLOW_IN;
- ObjectAnimator anim =
- ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, mProgress, targetProgress);
+ Animator anim = new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS_SPRING,
+ "allAppsSpringFromAATC", 1f / mShiftRange, mProgress, targetProgress);
anim.setDuration(config.duration);
anim.setInterpolator(builder.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
anim.addListener(getProgressAnimatorListener());
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 819c843..62f59e4 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -16,6 +16,7 @@
package com.android.launcher3.anim;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
@@ -23,10 +24,16 @@
import android.animation.AnimatorSet;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
+import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.SpringAnimation;
/**
* Helper class to control the playback of an {@link AnimatorSet}, with custom interpolators
@@ -37,6 +44,9 @@
*/
public abstract class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateListener {
+ private static final String TAG = "AnimatorPlaybackCtrler";
+ private static boolean DEBUG = false;
+
public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) {
return wrap(anim, duration, null);
}
@@ -60,6 +70,7 @@
private final long mDuration;
protected final AnimatorSet mAnim;
+ private Set<SpringAnimation> mSprings;
protected float mCurrentFraction;
private Runnable mEndAction;
@@ -67,6 +78,9 @@
protected boolean mTargetCancelled = false;
protected Runnable mOnCancelRunnable;
+ private OnAnimationEndDispatcher mEndListener;
+ private DynamicAnimation.OnAnimationEndListener mSpringEndListener;
+
protected AnimatorPlaybackController(AnimatorSet anim, long duration,
Runnable onCancelRunnable) {
mAnim = anim;
@@ -75,7 +89,8 @@
mAnimationPlayer = ValueAnimator.ofFloat(0, 1);
mAnimationPlayer.setInterpolator(LINEAR);
- mAnimationPlayer.addListener(new OnAnimationEndDispatcher());
+ mEndListener = new OnAnimationEndDispatcher();
+ mAnimationPlayer.addListener(mEndListener);
mAnimationPlayer.addUpdateListener(this);
mAnim.addListener(new AnimatorListenerAdapter() {
@@ -99,6 +114,15 @@
mTargetCancelled = false;
}
});
+
+ mSprings = new HashSet<>();
+ mSpringEndListener = (animation, canceled, value, velocity1) -> {
+ if (canceled) {
+ mEndListener.onAnimationCancel(mAnimationPlayer);
+ } else {
+ mEndListener.onAnimationEnd(mAnimationPlayer);
+ }
+ };
}
public AnimatorSet getTarget() {
@@ -180,6 +204,29 @@
}
}
+ /**
+ * Starts playback and sets the spring.
+ */
+ public void dispatchOnStartWithVelocity(float end, float velocity) {
+ if (!QUICKSTEP_SPRINGS.get()) {
+ dispatchOnStart();
+ return;
+ }
+
+ if (DEBUG) Log.d(TAG, "dispatchOnStartWithVelocity#end=" + end + ", velocity=" + velocity);
+
+ for (Animator a : mAnim.getChildAnimations()) {
+ if (a instanceof SpringObjectAnimator) {
+ if (DEBUG) Log.d(TAG, "Found springAnimator=" + a);
+ SpringObjectAnimator springAnimator = (SpringObjectAnimator) a;
+ mSprings.add(springAnimator.getSpring());
+ springAnimator.startSpring(end, velocity, mSpringEndListener);
+ }
+ }
+
+ dispatchOnStart();
+ }
+
public void dispatchOnStart() {
dispatchOnStartRecursively(mAnim);
}
@@ -282,6 +329,18 @@
}
}
+ private boolean isAnySpringRunning() {
+ for (SpringAnimation spring : mSprings) {
+ if (spring.isRunning()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Only dispatches the on end actions once the animator and all springs have completed running.
+ */
private class OnAnimationEndDispatcher extends AnimationSuccessListener {
@Override
@@ -291,9 +350,12 @@
@Override
public void onAnimationSuccess(Animator animator) {
- dispatchOnEndRecursively(mAnim);
- if (mEndAction != null) {
- mEndAction.run();
+ // We wait for the spring (if any) to finish running before completing the end callback.
+ if (mSprings.isEmpty() || !isAnySpringRunning()) {
+ dispatchOnEndRecursively(mAnim);
+ if (mEndAction != null) {
+ mEndAction.run();
+ }
}
}
diff --git a/src/com/android/launcher3/anim/SpringObjectAnimator.java b/src/com/android/launcher3/anim/SpringObjectAnimator.java
new file mode 100644
index 0000000..4ece909
--- /dev/null
+++ b/src/com/android/launcher3/anim/SpringObjectAnimator.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.anim;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.util.Log;
+import android.util.Property;
+
+import com.android.launcher3.ProgressInterface;
+
+import java.util.ArrayList;
+
+import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+
+/**
+ * This animator allows for an object's property to be be controlled by an {@link ObjectAnimator} or
+ * a {@link SpringAnimation}. It extends ValueAnimator so it can be used in an AnimatorSet.
+ */
+public class SpringObjectAnimator<T extends ProgressInterface> extends ValueAnimator {
+
+ private static final String TAG = "SpringObjectAnimator";
+ private static boolean DEBUG = false;
+
+ private T mObject;
+ private ObjectAnimator mObjectAnimator;
+ private float[] mValues;
+
+ private SpringAnimation mSpring;
+ private SpringProperty<T> mProperty;
+
+ private ArrayList<AnimatorListener> mListeners;
+ private boolean mSpringEnded = false;
+ private boolean mAnimatorEnded = false;
+ private boolean mEnded = false;
+
+ private static final float SPRING_DAMPING_RATIO = 0.9f;
+ private static final float SPRING_STIFFNESS = 600f;
+
+ public SpringObjectAnimator(T object, FloatPropertyCompat<T> floatProperty,
+ String name, float minimumVisibleChange, float... values) {
+ mObject = object;
+ mSpring = new SpringAnimation(object, floatProperty);
+ mSpring.setMinimumVisibleChange(minimumVisibleChange);
+ mSpring.setSpring(new SpringForce(0)
+ .setDampingRatio(SPRING_DAMPING_RATIO)
+ .setStiffness(SPRING_STIFFNESS));
+ mSpring.setStartVelocity(0.01f);
+ mProperty = new SpringProperty<T>(name, mSpring);
+ mObjectAnimator = ObjectAnimator.ofFloat(object, mProperty, values);
+ mValues = values;
+ mListeners = new ArrayList<>();
+ setFloatValues(values);
+
+ mObjectAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mAnimatorEnded = false;
+ mEnded = false;
+ for (AnimatorListener l : mListeners) {
+ l.onAnimationStart(animation);
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimatorEnded = true;
+ tryEnding();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ for (AnimatorListener l : mListeners) {
+ l.onAnimationCancel(animation);
+ }
+ mSpring.animateToFinalPosition(mObject.getProgress());
+ }
+ });
+
+ mSpring.addUpdateListener((animation, value, velocity) -> mSpringEnded = false);
+ mSpring.addEndListener((animation, canceled, value, velocity) -> {
+ mSpringEnded = true;
+ tryEnding();
+ });
+ }
+
+ private void tryEnding() {
+ if (DEBUG) {
+ Log.d(TAG, "tryEnding#mAnimatorEnded=" + mAnimatorEnded + ", mSpringEnded="
+ + mSpringEnded + ", mEnded=" + mEnded);
+ }
+
+ // If springs are disabled, ignore value of mSpringEnded
+ if (mAnimatorEnded && (mSpringEnded || !QUICKSTEP_SPRINGS.get()) && !mEnded) {
+ for (AnimatorListener l : mListeners) {
+ l.onAnimationEnd(this);
+ }
+ mEnded = true;
+ }
+ }
+
+ public SpringAnimation getSpring() {
+ return mSpring;
+ }
+
+ /**
+ * Initializes and sets up the spring to take over controlling the object.
+ */
+ void startSpring(float end, float velocity, OnAnimationEndListener endListener) {
+ // Cancel the spring so we can set new start velocity and final position. We need to remove
+ // the listener since the spring is not actually ending.
+ mSpring.removeEndListener(endListener);
+ mSpring.cancel();
+ mSpring.addEndListener(endListener);
+
+ mProperty.switchToSpring();
+
+ mSpring.setStartVelocity(velocity);
+ mSpring.animateToFinalPosition(end == 0 ? mValues[0] : mValues[1]);
+ }
+
+ @Override
+ public void addListener(AnimatorListener listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public ArrayList<AnimatorListener> getListeners() {
+ return mListeners;
+ }
+
+ @Override
+ public void removeAllListeners() {
+ mListeners.clear();
+ }
+
+ @Override
+ public void removeListener(AnimatorListener listener) {
+ mListeners.remove(listener);
+ }
+
+ @Override
+ public void addPauseListener(AnimatorPauseListener listener) {
+ mObjectAnimator.addPauseListener(listener);
+ }
+
+ @Override
+ public void cancel() {
+ mSpring.animateToFinalPosition(mObject.getProgress());
+ mObjectAnimator.cancel();
+ }
+
+ @Override
+ public void end() {
+ mObjectAnimator.end();
+ }
+
+ @Override
+ public long getDuration() {
+ return mObjectAnimator.getDuration();
+ }
+
+ @Override
+ public TimeInterpolator getInterpolator() {
+ return mObjectAnimator.getInterpolator();
+ }
+
+ @Override
+ public long getStartDelay() {
+ return mObjectAnimator.getStartDelay();
+ }
+
+ @Override
+ public long getTotalDuration() {
+ return mObjectAnimator.getTotalDuration();
+ }
+
+ @Override
+ public boolean isPaused() {
+ return mObjectAnimator.isPaused();
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mObjectAnimator.isRunning();
+ }
+
+ @Override
+ public boolean isStarted() {
+ return mObjectAnimator.isStarted();
+ }
+
+ @Override
+ public void pause() {
+ mObjectAnimator.pause();
+ }
+
+ @Override
+ public void removePauseListener(AnimatorPauseListener listener) {
+ mObjectAnimator.removePauseListener(listener);
+ }
+
+ @Override
+ public void resume() {
+ mObjectAnimator.resume();
+ }
+
+ @Override
+ public ValueAnimator setDuration(long duration) {
+ return mObjectAnimator.setDuration(duration);
+ }
+
+ @Override
+ public void setInterpolator(TimeInterpolator value) {
+ mObjectAnimator.setInterpolator(value);
+ }
+
+ @Override
+ public void setStartDelay(long startDelay) {
+ mObjectAnimator.setStartDelay(startDelay);
+ }
+
+ @Override
+ public void setTarget(Object target) {
+ mObjectAnimator.setTarget(target);
+ }
+
+ @Override
+ public void start() {
+ mObjectAnimator.start();
+ }
+
+ @Override
+ public void setCurrentFraction(float fraction) {
+ mObjectAnimator.setCurrentFraction(fraction);
+ }
+
+ @Override
+ public void setCurrentPlayTime(long playTime) {
+ mObjectAnimator.setCurrentPlayTime(playTime);
+ }
+
+ public static class SpringProperty<T extends ProgressInterface> extends Property<T, Float> {
+
+ boolean useSpring = false;
+ final SpringAnimation mSpring;
+
+ public SpringProperty(String name, SpringAnimation spring) {
+ super(Float.class, name);
+ mSpring = spring;
+ }
+
+ public void switchToSpring() {
+ useSpring = true;
+ }
+
+ @Override
+ public Float get(T object) {
+ return object.getProgress();
+ }
+
+ @Override
+ public void set(T object, Float progress) {
+ if (useSpring) {
+ mSpring.animateToFinalPosition(progress);
+ } else {
+ object.setProgress(progress);
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
index 84e82e3..a7c0a47 100644
--- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
+++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
@@ -12,6 +12,8 @@
import java.lang.reflect.Method;
import java.util.Locale;
+import androidx.annotation.NonNull;
+
public class AlphabeticIndexCompat {
private static final String TAG = "AlphabeticIndexCompat";
@@ -53,7 +55,7 @@
/**
* Computes the section name for an given string {@param s}.
*/
- public String computeSectionName(CharSequence cs) {
+ public String computeSectionName(@NonNull CharSequence cs) {
String s = Utilities.trim(cs);
String sectionName = mBaseIndex.getBucketLabel(mBaseIndex.getBucketIndex(s));
if (Utilities.trim(sectionName).isEmpty() && s.length() > 0) {
@@ -89,7 +91,7 @@
/**
* Returns the index of the bucket in which the given string should appear.
*/
- protected int getBucketIndex(String s) {
+ protected int getBucketIndex(@NonNull String s) {
if (s.isEmpty()) {
return UNKNOWN_BUCKET_INDEX;
}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 03fdc64..fa93081 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -95,8 +95,19 @@
public static final TogglableFlag APPLY_CONFIG_AT_RUNTIME = new TogglableFlag(
"APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
- public static final TogglableFlag ENABLE_TASK_STABILIZER = new TogglableFlag(
- "ENABLE_TASK_STABILIZER", false, "Stable task list across fast task switches");
+ public static final ToggleableGlobalSettingsFlag ENABLE_TASK_STABILIZER
+ = new ToggleableGlobalSettingsFlag("ENABLE_TASK_STABILIZER", false,
+ "Stable task list across fast task switches");
+
+ public static final TogglableFlag QUICKSTEP_SPRINGS = new TogglableFlag("QUICKSTEP_SPRINGS",
+ false, "Enable springs for quickstep animations");
+
+ public static final TogglableFlag ENABLE_QUICKSTEP_LIVE_TILE = new TogglableFlag(
+ "ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
+
+ public static final ToggleableGlobalSettingsFlag SWIPE_HOME
+ = new ToggleableGlobalSettingsFlag("SWIPE_HOME", false,
+ "[WIP] Swiping up on the nav bar goes home. Swipe and hold goes to recent apps.");
public static void initialize(Context context) {
// Avoid the disk read for user builds
diff --git a/src/com/android/launcher3/folder/FolderShape.java b/src/com/android/launcher3/folder/FolderShape.java
index ae279cb..4b06dda 100644
--- a/src/com/android/launcher3/folder/FolderShape.java
+++ b/src/com/android/launcher3/folder/FolderShape.java
@@ -23,6 +23,9 @@
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -34,13 +37,29 @@
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.util.Xml;
import android.view.ViewOutlineProvider;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.Themes;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.annotation.Nullable;
/**
* Abstract representation of the shape of a folder icon
@@ -53,15 +72,7 @@
return sInstance;
}
- private static FolderShape[] getAllShapes() {
- return new FolderShape[] {
- new Circle(),
- new RoundedSquare(8f / 50), // Ratios based on path defined in config_icon_mask
- new RoundedSquare(30f / 50),
- new Square(),
- new TearDrop(),
- new Squircle()};
- }
+ private SparseArray<TypedValue> mAttrs;
public abstract void drawShape(Canvas canvas, float offsetX, float offsetY, float radius,
Paint paint);
@@ -71,6 +82,11 @@
public abstract Animator createRevealAnimator(Folder target, Rect startRect, Rect endRect,
float endRadius, boolean isReversed);
+ @Nullable
+ public TypedValue getAttrValue(int attr) {
+ return mAttrs == null ? null : mAttrs.get(attr);
+ }
+
/**
* Abstract shape where the reveal animation is a derivative of a round rect animation
*/
@@ -163,44 +179,22 @@
}
}
- public static class Square extends SimpleRectShape {
-
- @Override
- public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint p) {
- float cx = radius + offsetX;
- float cy = radius + offsetY;
- canvas.drawRect(cx - radius, cy - radius, cx + radius, cy + radius, p);
- }
-
- @Override
- public void addShape(Path path, float offsetX, float offsetY, float radius) {
- float cx = radius + offsetX;
- float cy = radius + offsetY;
- path.addRect(cx - radius, cy - radius, cx + radius, cy + radius, Path.Direction.CW);
- }
-
- @Override
- protected float getStartRadius(Rect startRect) {
- return 0;
- }
- }
-
public static class RoundedSquare extends SimpleRectShape {
/**
- * Ratio of corner radius to half size. Based on the
+ * Ratio of corner radius to half size.
*/
- private final float mRadiusFactor;
+ private final float mRadiusRatio;
- public RoundedSquare(float radiusFactor) {
- mRadiusFactor = radiusFactor;
+ public RoundedSquare(float radiusRatio) {
+ mRadiusRatio = radiusRatio;
}
@Override
public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint p) {
float cx = radius + offsetX;
float cy = radius + offsetY;
- float cr = radius * mRadiusFactor;
+ float cr = radius * mRadiusRatio;
canvas.drawRoundRect(cx - radius, cy - radius, cx + radius, cy + radius, cr, cr, p);
}
@@ -208,14 +202,14 @@
public void addShape(Path path, float offsetX, float offsetY, float radius) {
float cx = radius + offsetX;
float cy = radius + offsetY;
- float cr = radius * mRadiusFactor;
+ float cr = radius * mRadiusRatio;
path.addRoundRect(cx - radius, cy - radius, cx + radius, cy + radius, cr, cr,
Path.Direction.CW);
}
@Override
protected float getStartRadius(Rect startRect) {
- return (startRect.width() / 2f) * mRadiusFactor;
+ return (startRect.width() / 2f) * mRadiusRatio;
}
}
@@ -224,13 +218,16 @@
/**
* Radio of short radius to large radius, based on the shape options defined in the config.
*/
- private static final float RADIUS_RATIO = 15f / 50;
-
+ private final float mRadiusRatio;
private final float[] mTempRadii = new float[8];
+ public TearDrop(float radiusRatio) {
+ mRadiusRatio = radiusRatio;
+ }
+
@Override
public void addShape(Path p, float offsetX, float offsetY, float r1) {
- float r2 = r1 * RADIUS_RATIO;
+ float r2 = r1 * mRadiusRatio;
float cx = r1 + offsetX;
float cy = r1 + offsetY;
@@ -249,7 +246,7 @@
protected AnimatorUpdateListener newUpdateListener(Rect startRect, Rect endRect,
float endRadius, Path outPath) {
float r1 = startRect.width() / 2f;
- float r2 = r1 * RADIUS_RATIO;
+ float r2 = r1 * mRadiusRatio;
float[] startValues = new float[] {
startRect.left, startRect.top, startRect.right, startRect.bottom, r1, r2};
@@ -273,13 +270,17 @@
/**
* Radio of radius to circle radius, based on the shape options defined in the config.
*/
- private static final float RADIUS_RATIO = 10f / 50;
+ private final float mRadiusRatio;
+
+ public Squircle(float radiusRatio) {
+ mRadiusRatio = radiusRatio;
+ }
@Override
public void addShape(Path p, float offsetX, float offsetY, float r) {
float cx = r + offsetX;
float cy = r + offsetY;
- float control = r - r * RADIUS_RATIO;
+ float control = r - r * mRadiusRatio;
p.moveTo(cx, cy - r);
addLeftCurve(cx, cy, r, control, p);
@@ -310,7 +311,7 @@
float startCX = startRect.exactCenterX();
float startCY = startRect.exactCenterY();
float startR = startRect.width() / 2f;
- float startControl = startR - startR * RADIUS_RATIO;
+ float startControl = startR - startR * mRadiusRatio;
float startHShift = 0;
float startVShift = 0;
@@ -351,17 +352,65 @@
}
/**
- * Initializes the shape which is closest to closest to the {@link AdaptiveIconDrawable}
+ * Initializes the shape which is closest to the {@link AdaptiveIconDrawable}
*/
- public static void init() {
+ public static void init(Context context) {
if (!Utilities.ATLEAST_OREO) {
return;
}
- new MainThreadExecutor().execute(FolderShape::pickShapeInBackground);
+ new MainThreadExecutor().execute(() -> pickShapeInBackground(context));
+ }
+
+ private static FolderShape getShapeDefinition(String type, float radius) {
+ switch (type) {
+ case "Circle":
+ return new Circle();
+ case "RoundedSquare":
+ return new RoundedSquare(radius);
+ case "TearDrop":
+ return new TearDrop(radius);
+ case "Squircle":
+ return new Squircle(radius);
+ default:
+ throw new IllegalArgumentException("Invalid shape type: " + type);
+ }
+ }
+
+ private static List<FolderShape> getAllShapes(Context context) {
+ ArrayList<FolderShape> result = new ArrayList<>();
+ try (XmlResourceParser parser = context.getResources().getXml(R.xml.folder_shapes)) {
+
+ // Find the root tag
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_TAG
+ && type != XmlPullParser.END_DOCUMENT
+ && !"shapes".equals(parser.getName()));
+
+ final int depth = parser.getDepth();
+ int[] radiusAttr = new int[] {R.attr.folderIconRadius};
+ IntArray keysToIgnore = new IntArray(0);
+
+ while (((type = parser.next()) != XmlPullParser.END_TAG ||
+ parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+
+ if (type == XmlPullParser.START_TAG) {
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+ TypedArray a = context.obtainStyledAttributes(attrs, radiusAttr);
+ FolderShape shape = getShapeDefinition(parser.getName(), a.getFloat(0, 1));
+ a.recycle();
+
+ shape.mAttrs = Themes.createValueMap(context, attrs, keysToIgnore);
+ result.add(shape);
+ }
+ }
+ } catch (IOException | XmlPullParserException e) {
+ throw new RuntimeException(e);
+ }
+ return result;
}
@TargetApi(Build.VERSION_CODES.O)
- protected static void pickShapeInBackground() {
+ protected static void pickShapeInBackground(Context context) {
// Pick any large size
int size = 200;
@@ -379,7 +428,7 @@
// Find the shape with minimum area of divergent region.
int minArea = Integer.MAX_VALUE;
FolderShape closestShape = null;
- for (FolderShape shape : getAllShapes()) {
+ for (FolderShape shape : getAllShapes(context)) {
shapePath.reset();
shape.addShape(shapePath, 0, 0, size / 2f);
shapeR.setPath(shapePath, full);
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 75d3425..7eb4015 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -36,6 +36,7 @@
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
import java.nio.ByteBuffer;
@@ -131,9 +132,15 @@
width = (int) (mView.getWidth() * scale);
height = (int) (mView.getHeight() * scale);
- // Use software renderer for widgets as we know that they already work
- return BitmapRenderer.createSoftwareBitmap(width + blurSizeOutline,
- height + blurSizeOutline, (c) -> drawDragView(c, scale));
+ if (mView instanceof PendingAppWidgetHostView) {
+ // Use hardware renderer as the icon for the pending app widget may be a hw bitmap
+ return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
+ height + blurSizeOutline, (c) -> drawDragView(c, scale));
+ } else {
+ // Use software renderer for widgets as we know that they already work
+ return BitmapRenderer.createSoftwareBitmap(width + blurSizeOutline,
+ height + blurSizeOutline, (c) -> drawDragView(c, scale));
+ }
}
return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
diff --git a/src/com/android/launcher3/graphics/GridOptionsProvider.java b/src/com/android/launcher3/graphics/GridOptionsProvider.java
new file mode 100644
index 0000000..efd39ee
--- /dev/null
+++ b/src/com/android/launcher3/graphics/GridOptionsProvider.java
@@ -0,0 +1,191 @@
+package com.android.launcher3.graphics;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.res.XmlResourceParser;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile.GridOption;
+import com.android.launcher3.R;
+import com.android.launcher3.util.LooperExecutor;
+import com.android.launcher3.util.UiThreadHelper;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Future;
+
+/**
+ * Exposes various launcher grid options and allows the caller to change them.
+ * APIs:
+ * /list_options: List the various available grip options, has following columns
+ * name: name of the grid
+ * rows: number of rows in the grid
+ * cols: number of columns in the grid
+ * preview_count: number of previews available for this grid option. The preview uri
+ * looks like /preview/<grid-name>/<preview index starting with 0>
+ * is_default: true if this grid is currently active
+ *
+ * /preview: Opens a file stream for the grid preview
+ *
+ * /default_grid: Call update to set the current grid, with values
+ * name: name of the grid to apply
+ */
+public class GridOptionsProvider extends ContentProvider {
+
+ private static final String TAG = "GridOptionsProvider";
+
+ private static final String KEY_NAME = "name";
+ private static final String KEY_ROWS = "rows";
+ private static final String KEY_COLS = "cols";
+ private static final String KEY_PREVIEW_COUNT = "preview_count";
+ private static final String KEY_IS_DEFAULT = "is_default";
+
+ private static final String KEY_LIST_OPTIONS = "/list_options";
+ private static final String KEY_DEFAULT_GRID = "/default_grid";
+
+ private static final String KEY_PREVIEW = "preview";
+ private static final String MIME_TYPE_PNG = "image/png";
+
+ public static final PipeDataWriter<Future<Bitmap>> BITMAP_WRITER =
+ new PipeDataWriter<Future<Bitmap>>() {
+ @Override
+ public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String s,
+ Bundle bundle, Future<Bitmap> bitmap) {
+ try (AutoCloseOutputStream os = new AutoCloseOutputStream(output)) {
+ bitmap.get().compress(Bitmap.CompressFormat.PNG, 100, os);
+ } catch (Exception e) {
+ Log.w(TAG, "fail to write to pipe", e);
+ }
+ }
+ };
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ if (!KEY_LIST_OPTIONS.equals(uri.getPath())) {
+ return null;
+ }
+ MatrixCursor cursor = new MatrixCursor(new String[] {
+ KEY_NAME, KEY_ROWS, KEY_COLS, KEY_PREVIEW_COUNT, KEY_IS_DEFAULT});
+ InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext());
+ for (GridOption gridOption : parseAllGridOptions()) {
+ cursor.newRow()
+ .add(KEY_NAME, gridOption.name)
+ .add(KEY_ROWS, gridOption.numRows)
+ .add(KEY_COLS, gridOption.numColumns)
+ .add(KEY_PREVIEW_COUNT, 1)
+ .add(KEY_IS_DEFAULT, idp.numColumns == gridOption.numColumns
+ && idp.numRows == gridOption.numRows);
+ }
+ return cursor;
+ }
+
+ private List<GridOption> parseAllGridOptions() {
+ List<GridOption> result = new ArrayList<>();
+ try (XmlResourceParser parser = getContext().getResources().getXml(R.xml.device_profiles)) {
+ final int depth = parser.getDepth();
+ int type;
+ while (((type = parser.next()) != XmlPullParser.END_TAG ||
+ parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+ if ((type == XmlPullParser.START_TAG)
+ && GridOption.TAG_NAME.equals(parser.getName())) {
+ result.add(new GridOption(getContext(), Xml.asAttributeSet(parser)));
+ }
+ }
+ } catch (IOException | XmlPullParserException e) {
+ Log.e(TAG, "Error parsing device profile", e);
+ return Collections.emptyList();
+ }
+ return result;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ List<String> segments = uri.getPathSegments();
+ if (segments.size() > 0 && KEY_PREVIEW.equals(segments.get(0))) {
+ return MIME_TYPE_PNG;
+ }
+ return "vnd.android.cursor.dir/launcher_grid";
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues initialValues) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ if (!KEY_DEFAULT_GRID.equals(uri.getPath())) {
+ return 0;
+ }
+
+ String gridName = values.getAsString(KEY_NAME);
+ // Verify that this is a valid grid option
+ GridOption match = null;
+ for (GridOption option : parseAllGridOptions()) {
+ if (option.name.equals(gridName)) {
+ match = option;
+ break;
+ }
+ }
+ if (match == null) {
+ return 0;
+ }
+
+ InvariantDeviceProfile.INSTANCE.get(getContext()).setCurrentGrid(getContext(), gridName);
+ return 1;
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ List<String> segments = uri.getPathSegments();
+ if (segments.size() < 2 || !KEY_PREVIEW.equals(segments.get(0))) {
+ throw new FileNotFoundException("Invalid preview url");
+ }
+ String profileName = segments.get(1);
+ if (TextUtils.isEmpty(profileName)) {
+ throw new FileNotFoundException("Invalid preview url");
+ }
+
+ InvariantDeviceProfile idp;
+ try {
+ idp = new InvariantDeviceProfile(getContext(), profileName);
+ } catch (Exception e) {
+ throw new FileNotFoundException(e.getMessage());
+ }
+
+ LooperExecutor executor = new LooperExecutor(UiThreadHelper.getBackgroundLooper());
+ try {
+ return openPipeHelper(uri, MIME_TYPE_PNG, null,
+ executor.submit(new LauncherPreviewRenderer(getContext(), idp)), BITMAP_WRITER);
+ } catch (Exception e) {
+ throw new FileNotFoundException(e.getMessage());
+ }
+ }
+}
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index dc6f50f..837749d 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -21,6 +21,7 @@
import android.annotation.TargetApi;
import android.app.Fragment;
+import android.app.WallpaperManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
@@ -29,6 +30,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Handler;
@@ -61,6 +63,7 @@
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
+import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
/**
@@ -72,7 +75,7 @@
* 4) Measure and draw the view on a canvas
*/
@TargetApi(Build.VERSION_CODES.O)
-public class LauncherPreviewRenderer {
+public class LauncherPreviewRenderer implements Callable<Bitmap> {
private static final String TAG = "LauncherPreviewRenderer";
@@ -110,7 +113,8 @@
context.getString(R.string.label_application);
}
- public Bitmap createScreenShot() {
+ @Override
+ public Bitmap call() {
return BitmapRenderer.createHardwareBitmap(mDp.widthPx, mDp.heightPx, c -> {
if (Looper.myLooper() == Looper.getMainLooper()) {
@@ -279,7 +283,6 @@
// Additional measure for views which use auto text size API
measureView(mRootView, mDp.widthPx, mDp.heightPx);
- canvas.drawColor(Color.GRAY);
mRootView.draw(canvas);
dispatchVisibilityAggregated(mRootView, false);
}
diff --git a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
index 66f9dbf..6fac31e2 100644
--- a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
+++ b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
@@ -16,16 +16,9 @@
package com.android.launcher3.graphics;
-import static android.content.Intent.ACTION_SCREEN_OFF;
-import static android.content.Intent.ACTION_USER_PRESENT;
-
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import android.animation.ObjectAnimator;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -96,20 +89,6 @@
}
};
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (ACTION_SCREEN_OFF.equals(action)) {
- mAnimateScrimOnNextDraw = true;
- } else if (ACTION_USER_PRESENT.equals(action)) {
- // ACTION_USER_PRESENT is sent after onStart/onResume. This covers the case where
- // the user unlocked and the Launcher is not in the foreground.
- mAnimateScrimOnNextDraw = false;
- }
- }
- };
-
private static final int DARK_SCRIM_COLOR = 0x55000000;
private static final int MAX_HOTSEAT_SCRIM_ALPHA = 100;
private static final int ALPHA_MASK_HEIGHT_DP = 500;
@@ -225,20 +204,11 @@
public void onViewAttachedToWindow(View view) {
mWallpaperColorInfo.addOnChangeListener(this);
onExtractedColorsChanged(mWallpaperColorInfo);
-
- if (mTopScrim != null) {
- IntentFilter filter = new IntentFilter(ACTION_SCREEN_OFF);
- filter.addAction(ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone
- mRoot.getContext().registerReceiver(mReceiver, filter);
- }
}
@Override
public void onViewDetachedFromWindow(View view) {
mWallpaperColorInfo.removeOnChangeListener(this);
- if (mTopScrim != null) {
- mRoot.getContext().unregisterReceiver(mReceiver);
- }
}
@Override
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 5fc5551..0d55301 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -62,7 +62,7 @@
final IntArray addedWorkspaceScreensFinal = new IntArray();
synchronized(dataModel) {
- IntArray workspaceScreens = dataModel.workspaceScreens.clone();
+ IntArray workspaceScreens = dataModel.collectWorkspaceScreens();
List<ItemInfo> filteredItems = new ArrayList<>();
for (Pair<ItemInfo, Object> entry : mItemList) {
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index f6e220f..23c6faf 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -92,7 +92,7 @@
synchronized (mBgDataModel) {
workspaceItems.addAll(mBgDataModel.workspaceItems);
appWidgets.addAll(mBgDataModel.appWidgets);
- orderedScreenIds.addAll(mBgDataModel.workspaceScreens);
+ orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
mBgDataModel.lastBindId++;
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 151d6f4..b338fff 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -27,6 +27,7 @@
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.DumpTargetWrapper;
import com.android.launcher3.model.nano.LauncherDumpProto;
@@ -37,6 +38,7 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.IntSparseArrayMap;
import com.google.protobuf.nano.MessageNano;
@@ -81,11 +83,6 @@
public final IntSparseArrayMap<FolderInfo> folders = new IntSparseArrayMap<>();
/**
- * Ordered list of workspace screens ids.
- */
- public final IntArray workspaceScreens = new IntArray();
-
- /**
* Map of ShortcutKey to the number of times it is pinned.
*/
public final Map<ShortcutKey, MutableInt> pinnedShortcutCounts = new HashMap<>();
@@ -118,11 +115,26 @@
appWidgets.clear();
folders.clear();
itemsIdMap.clear();
- workspaceScreens.clear();
pinnedShortcutCounts.clear();
deepShortcutMap.clear();
}
+ /**
+ * Creates an array of valid workspace screens based on current items in the model.
+ */
+ public synchronized IntArray collectWorkspaceScreens() {
+ IntSet screenSet = new IntSet();
+ for (ItemInfo item: itemsIdMap) {
+ if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ screenSet.add(item.screenId);
+ }
+ }
+ if (FeatureFlags.QSB_ON_FIRST_SCREEN.get() || screenSet.isEmpty()) {
+ screenSet.add(Workspace.FIRST_SCREEN_ID);
+ }
+ return screenSet.getArray();
+ }
+
public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer,
String[] args) {
if (Arrays.asList(args).contains("--proto")) {
@@ -130,11 +142,6 @@
return;
}
writer.println(prefix + "Data Model:");
- writer.print(prefix + " ---- workspace screens: ");
- for (int i = 0; i < workspaceScreens.size(); i++) {
- writer.print(" " + workspaceScreens.get(i));
- }
- writer.println();
writer.println(prefix + " ---- workspace items ");
for (int i = 0; i < workspaceItems.size(); i++) {
writer.println(prefix + '\t' + workspaceItems.get(i).toString());
@@ -167,6 +174,7 @@
// Add top parent nodes. (L1)
DumpTargetWrapper hotseat = new DumpTargetWrapper(ContainerType.HOTSEAT, 0);
IntSparseArrayMap<DumpTargetWrapper> workspaces = new IntSparseArrayMap<>();
+ IntArray workspaceScreens = collectWorkspaceScreens();
for (int i = 0; i < workspaceScreens.size(); i++) {
workspaces.put(workspaceScreens.get(i),
new DumpTargetWrapper(ContainerType.WORKSPACE, i));
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 8a6aa1af..cfabc10 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -31,7 +31,6 @@
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
-import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
@@ -43,19 +42,17 @@
import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
-import com.android.launcher3.ItemInfoWithIcon;
-import com.android.launcher3.icons.ComponentWithLabel;
-import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
-import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
-import com.android.launcher3.icons.IconCache;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.ItemInfo;
+import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
@@ -63,16 +60,18 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIconPreviewVerifier;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
+import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherActivtiyCachingLogic;
import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.provider.ImportDataTask;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.LooperIdleLock;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
@@ -158,9 +157,9 @@
allItems.addAll(mBgDataModel.workspaceItems);
allItems.addAll(mBgDataModel.appWidgets);
}
- int firstScreen = mBgDataModel.workspaceScreens.isEmpty()
- ? -1 // In this case, we can still look at the items in the hotseat.
- : mBgDataModel.workspaceScreens.get(0);
+ // Screen set is never empty
+ final int firstScreen = mBgDataModel.collectWorkspaceScreens().get(0);
+
filterCurrentWorkspaceItems(firstScreen, allItems, firstScreenItems,
new ArrayList<>() /* otherScreenItems are ignored */);
mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems);
@@ -784,16 +783,6 @@
null,
new Handler(LauncherModel.getWorkerLooper()));
}
-
- // Initialize the screens array. Using an InstSet ensures that the screen ids
- // are sorted.
- IntSet screenSet = new IntSet();
- for (ItemInfo item: mBgDataModel.itemsIdMap) {
- if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- screenSet.add(item.screenId);
- }
- }
- mBgDataModel.workspaceScreens.addAll(screenSet.getArray());
}
}
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 9c4a4ea..65103f6 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -29,6 +29,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.UiThreadHelper;
/**
* Utility class to manage launcher rotation
@@ -154,7 +155,7 @@
}
if (activityFlags != mLastActivityFlags) {
mLastActivityFlags = activityFlags;
- mActivity.setRequestedOrientation(activityFlags);
+ UiThreadHelper.setOrientationAsync(mActivity, activityFlags);
}
}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index a7bd243..bb14328 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT;
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -45,6 +46,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -428,8 +430,8 @@
maybeUpdateAtomicAnim(mFromState, targetState, targetState == mToState ? 1f : 0f);
updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()),
targetState, velocity, fling);
- mCurrentAnimation.dispatchOnStart();
- if (fling && targetState == LauncherState.ALL_APPS) {
+ mCurrentAnimation.dispatchOnStartWithVelocity(endProgress, velocity);
+ if (fling && targetState == LauncherState.ALL_APPS && !QUICKSTEP_SPRINGS.get()) {
mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
}
anim.start();
diff --git a/src/com/android/launcher3/util/RaceConditionTracker.java b/src/com/android/launcher3/util/RaceConditionTracker.java
index 8b06787..6954d0e 100644
--- a/src/com/android/launcher3/util/RaceConditionTracker.java
+++ b/src/com/android/launcher3/util/RaceConditionTracker.java
@@ -24,6 +24,8 @@
public class RaceConditionTracker {
public final static boolean ENTER = true;
public final static boolean EXIT = false;
+ static final String ENTER_POSTFIX = "enter";
+ static final String EXIT_POSTFIX = "exit";
public interface EventProcessor {
void onEvent(String eventName);
@@ -46,7 +48,7 @@
}
public static String enterExitEvt(String eventName, boolean isEnter) {
- return eventName + ":" + (isEnter ? "enter" : "exit");
+ return eventName + ":" + (isEnter ? ENTER_POSTFIX : EXIT_POSTFIX);
}
public static String enterEvt(String eventName) {
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index 5f965a3..675e2f4 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -21,6 +21,9 @@
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.util.TypedValue;
/**
* Various utility methods associated with theming.
@@ -104,4 +107,29 @@
target.getArray()[14] = Color.blue(dstColor) - Color.blue(srcColor);
target.getArray()[19] = Color.alpha(dstColor) - Color.alpha(srcColor);
}
+
+ /**
+ * Creates a map for attribute-name to value for all the values in {@param attrs} which can be
+ * held in memory for later use.
+ */
+ public static SparseArray<TypedValue> createValueMap(Context context, AttributeSet attrSet,
+ IntArray keysToIgnore) {
+ int count = attrSet.getAttributeCount();
+ IntArray attrNameArray = new IntArray(count);
+ for (int i = 0; i < count; i++) {
+ attrNameArray.add(attrSet.getAttributeNameResource(i));
+ }
+ attrNameArray.removeAllValues(keysToIgnore);
+
+ int[] attrNames = attrNameArray.toArray();
+ SparseArray<TypedValue> result = new SparseArray<>(attrNames.length);
+ TypedArray ta = context.obtainStyledAttributes(attrSet, attrNames);
+ for (int i = 0; i < attrNames.length; i++) {
+ TypedValue tv = new TypedValue();
+ ta.getValue(i, tv);
+ result.put(attrNames[i], tv);
+ }
+
+ return result;
+ }
}
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
index 27140a1..cc442f9 100644
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.util;
+import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
@@ -33,6 +34,8 @@
private static Handler sHandler;
private static final int MSG_HIDE_KEYBOARD = 1;
+ private static final int MSG_SET_ORIENTATION = 2;
+ private static final int MSG_RUN_COMMAND = 3;
public static Looper getBackgroundLooper() {
if (sHandlerThread == null) {
@@ -55,6 +58,15 @@
Message.obtain(getHandler(context), MSG_HIDE_KEYBOARD, token).sendToTarget();
}
+ public static void setOrientationAsync(Activity activity, int orientation) {
+ Message.obtain(getHandler(activity), MSG_SET_ORIENTATION, orientation, 0, activity)
+ .sendToTarget();
+ }
+
+ public static void runAsyncCommand(Context context, AsyncCommand command, int arg1, int arg2) {
+ Message.obtain(getHandler(context), MSG_RUN_COMMAND, arg1, arg2, command).sendToTarget();
+ }
+
private static class UiCallbacks implements Handler.Callback {
private final InputMethodManager mIMM;
@@ -69,8 +81,19 @@
case MSG_HIDE_KEYBOARD:
mIMM.hideSoftInputFromWindow((IBinder) message.obj, 0);
return true;
+ case MSG_SET_ORIENTATION:
+ ((Activity) message.obj).setRequestedOrientation(message.arg1);
+ return true;
+ case MSG_RUN_COMMAND:
+ ((AsyncCommand) message.obj).execute(message.arg1, message.arg2);
+ return true;
}
return false;
}
}
+
+ public interface AsyncCommand {
+
+ void execute(int arg1, int arg2);
+ }
}
diff --git a/src/com/android/launcher3/util/ViewPool.java b/src/com/android/launcher3/util/ViewPool.java
new file mode 100644
index 0000000..8af048d
--- /dev/null
+++ b/src/com/android/launcher3/util/ViewPool.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.util.ViewPool.Reusable;
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
+/**
+ * Utility class to maintain a pool of reusable views.
+ * During initialization, views are inflated on the background thread.
+ */
+public class ViewPool<T extends View & Reusable> {
+
+ private final Object[] mPool;
+
+ private final LayoutInflater mInflater;
+ private final ViewGroup mParent;
+ private final int mLayoutId;
+
+ private int mCurrentSize = 0;
+
+ public ViewPool(Context context, @Nullable ViewGroup parent,
+ int layoutId, int maxSize, int initialSize) {
+ mLayoutId = layoutId;
+ mParent = parent;
+ mInflater = LayoutInflater.from(context);
+ mPool = new Object[maxSize];
+
+ if (initialSize > 0) {
+ initPool(initialSize);
+ }
+ }
+
+ @UiThread
+ private void initPool(int initialSize) {
+ Preconditions.assertUIThread();
+ Handler handler = new Handler();
+
+ // Inflate views on a non looper thread. This allows us to catch errors like calling
+ // "new Handler()" in constructor easily.
+ new Thread(() -> {
+ for (int i = 0; i < initialSize; i++) {
+ T view = inflateNewView();
+ handler.post(() -> addToPool(view));
+ }
+ }).start();
+ }
+
+ @UiThread
+ public void recycle(T view) {
+ Preconditions.assertUIThread();
+ view.onRecycle();
+ addToPool(view);
+ }
+
+ @UiThread
+ private void addToPool(T view) {
+ Preconditions.assertUIThread();
+ if (mCurrentSize >= mPool.length) {
+ // pool is full
+ return;
+ }
+
+ mPool[mCurrentSize] = view;
+ mCurrentSize++;
+ }
+
+ @UiThread
+ public T getView() {
+ Preconditions.assertUIThread();
+ if (mCurrentSize > 0) {
+ mCurrentSize--;
+ return (T) mPool[mCurrentSize];
+ }
+ return inflateNewView();
+ }
+
+ @AnyThread
+ private T inflateNewView() {
+ return (T) mInflater.inflate(mLayoutId, mParent, false);
+ }
+
+ /**
+ * Interface to indicate that a view is reusable
+ */
+ public interface Reusable {
+
+ /**
+ * Called when a view is recycled / added back to the pool
+ */
+ void onRecycle();
+ }
+}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 9a17ec6..7a7f828 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -69,7 +69,8 @@
WidgetItemComparator widgetComparator = new WidgetItemComparator();
for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : mWidgetsList.entrySet()) {
WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue());
- row.titleSectionName = indexer.computeSectionName(row.pkgItem.title);
+ row.titleSectionName = (row.pkgItem.title == null) ? "" :
+ indexer.computeSectionName(row.pkgItem.title);
Collections.sort(row.widgets, widgetComparator);
result.add(row);
}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 8c03e4b..1b34598 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -123,7 +123,7 @@
public void evaluate() throws Throwable {
try {
// Create launcher activity if necessary and bring it to the front.
- mDevice.pressHome();
+ mLauncher.pressHome();
waitForLauncherCondition("Launcher activity wasn't created",
launcher -> launcher != null);
diff --git a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
index 9354862..fdf87be 100644
--- a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
+++ b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
@@ -21,14 +21,12 @@
LauncherActivityInfo settingsApp = getSettingsApp();
clearHomescreen();
- mDevice.pressHome();
- mDevice.waitForIdle();
final String appName = settingsApp.getLabel().toString();
// 1. Open all apps and wait for load complete.
// 2. Drag icon to homescreen.
// 3. Verify that the icon works on homescreen.
- mLauncher.getWorkspace().
+ mLauncher.pressHome().
switchToAllApps().
getAppIcon(appName).
dragToWorkspace().
diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
index 316e40d..0235f95 100644
--- a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
+++ b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
@@ -16,6 +16,9 @@
package com.android.launcher3.util;
+import static com.android.launcher3.util.RaceConditionTracker.ENTER_POSTFIX;
+import static com.android.launcher3.util.RaceConditionTracker.EXIT_POSTFIX;
+
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -46,7 +49,7 @@
* If an event A occurs before event B in the sequence, this is how execution order looks like:
* Events: ... A ... B ...
* Events and instructions, guaranteed order:
- * (instructions executed prior to A) A ... B (instructions executed after B)
+ * (instructions executed prior to A) A ... B (instructions executed after B)
*
* Each iteration has 3 parts (phases).
* Phase 1. Picking a previously seen event subsequence that we believe can have previously unseen
@@ -58,6 +61,8 @@
* Phase 3. Releasing all threads and letting the test iteration run till its end.
*
* The iterations end when all seen paths have been declared “uncontinuable”.
+ *
+ * When we register event XXX:enter, we hold all other events until we register XXX:exit.
*/
public class RaceConditionReproducer implements RaceConditionTracker.EventProcessor {
private static final String TAG = "RaceConditionReproducer";
@@ -81,7 +86,7 @@
private final Map<String, EventNode> mNextEvents = new HashMap<>();
// Whether we believe that further iterations will not be able to add more events to
// mNextEvents.
- private boolean mStoppedAddingChildren = false;
+ private boolean mStoppedAddingChildren = true;
private void debugDump(StringBuilder sb, int indent, String name) {
for (int i = 0; i < indent; ++i) sb.append('.');
@@ -134,6 +139,8 @@
}
}
if (!mStoppedAddingChildren) {
+ // Mark that we have finished adding children. It will remain true if no new
+ // children are added, or will be set to false upon adding a new child.
mStoppedAddingChildren = true;
return true;
}
@@ -216,6 +223,7 @@
RaceConditionTracker.setEventProcessor(null);
runResumeAllEventsCallbackLocked();
assertTrue("Non-empty postponed events", mPostponedEvents.isEmpty());
+ assertTrue("Last registered event is :enter", lastEventAsEnter() == null);
// No events came after mLastRegisteredEvent. It doesn't make sense to come to it again
// because we won't see new continuations.
@@ -246,12 +254,36 @@
}
/**
+ * Returns whether the last event was not an XXX:enter, or this event is a matching XXX:exit.
+ */
+ private boolean canRegisterEventNowLocked(String event) {
+ final String lastEventAsEnter = lastEventAsEnter();
+ final String thisEventAsExit = eventAsExit(event);
+
+ if (lastEventAsEnter != null) {
+ if (!lastEventAsEnter.equals(thisEventAsExit)) {
+ assertTrue("YYY:exit after XXX:enter", thisEventAsExit == null);
+ // Last event was :enter, but this event is not :exit.
+ return false;
+ }
+ } else {
+ // Previous event was not :enter.
+ assertTrue(":exit after a non-enter event", thisEventAsExit == null);
+ }
+ return true;
+ }
+
+ /**
* Registers an event issued by the app and returns null or decides that the event must be
* postponed, and returns an object to wait on.
*/
private synchronized Semaphore tryRegisterEvent(String event) {
Log.d(TAG, "Event issued by the app: " + event);
+ if (!canRegisterEventNowLocked(event)) {
+ return createWaitObjectForPostponedEventLocked(event);
+ }
+
if (mRegisteredEventCount < mSequenceToFollow.size()) {
// We are in the first part of the iteration. We only register events that follow the
// mSequenceToFollow and postponing all other events.
@@ -288,9 +320,14 @@
return createWaitObjectForPostponedEventLocked(event);
}
} else {
- // The second phase of the iteration. We are past the growth point and register
+ // The third phase of the iteration. We are past the growth point and register
// everything that comes.
registerEventLocked(event);
+ // Register events that may have been postponed while waiting for an :exit event
+ // during the third phase. We don't do this if just registered event is :enter.
+ if (eventAsEnter(event) == null && mRegisteredEventCount > mSequenceToFollow.size()) {
+ registerPostponedEventsLocked(new HashSet<>(mPostponedEvents.keySet()));
+ }
}
return null;
}
@@ -347,6 +384,11 @@
private void registerPostponedEventsLocked(Collection<String> events) {
for (String event : events) {
registerPostponedEventLocked(event);
+ if (eventAsEnter(event) != null) {
+ // Once :enter is registered, switch to waiting for :exit to come. Won't register
+ // other postponed events.
+ break;
+ }
}
}
@@ -355,14 +397,51 @@
registerEventLocked(event);
}
+ /**
+ * If the last registered event was XXX:enter, returns XXX, otherwise, null.
+ */
+ private String lastEventAsEnter() {
+ return eventAsEnter(mCurrentSequence.substring(mCurrentSequence.lastIndexOf("|") + 1));
+ }
+
+ /**
+ * If the event is XXX:postfix, returns XXX, otherwise, null.
+ */
+ private static String prefixFromPostfixedEvent(String event, String postfix) {
+ final int columnPos = event.indexOf(':');
+ if (columnPos != -1 && postfix.equals(event.substring(columnPos + 1))) {
+ return event.substring(0, columnPos);
+ }
+ return null;
+ }
+
+ /**
+ * If the event is XXX:enter, returns XXX, otherwise, null.
+ */
+ private static String eventAsEnter(String event) {
+ return prefixFromPostfixedEvent(event, ENTER_POSTFIX);
+ }
+
+ /**
+ * If the event is XXX:exit, returns XXX, otherwise, null.
+ */
+ private static String eventAsExit(String event) {
+ return prefixFromPostfixedEvent(event, EXIT_POSTFIX);
+ }
+
private void registerEventLocked(String event) {
+ assertTrue(canRegisterEventNowLocked(event));
+
Log.d(TAG, "Actually registering event: " + event);
EventNode next = mLastRegisteredEvent.mNextEvents.get(event);
if (next == null) {
// This event wasn't seen after mLastRegisteredEvent.
next = new EventNode();
mLastRegisteredEvent.mNextEvents.put(event, next);
- mLastRegisteredEvent.mStoppedAddingChildren = false;
+ // The fact that we've added a new event after the previous one means that the
+ // previous event is still a growth point, unless this event is :exit, which means
+ // that the previous event is :enter.
+ mLastRegisteredEvent.mStoppedAddingChildren = eventAsExit(event) != null;
}
mLastRegisteredEvent = next;
@@ -371,12 +450,6 @@
if (mCurrentSequence.length() > 0) mCurrentSequence.append("|");
mCurrentSequence.append(event);
Log.d(TAG, "Repro sequence: " + mCurrentSequence);
-
- if (mRegisteredEventCount == mSequenceToFollow.size() + 1) {
- // We just entered the third phase of the iteration, i.e. registered an event after
- // the growth point. Now we can let go of all postponed events.
- runResumeAllEventsCallbackLocked();
- }
}
private void runResumeAllEventsCallbackLocked() {
diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java b/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
index 7dde5cc..3fc268e 100644
--- a/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
+++ b/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
@@ -77,6 +77,46 @@
}
@Test
+ @Ignore // The test is too long for continuous testing.
+ // 2 threads, 3 events, including enter-exit pairs each.
+ public void test3_3_enter_exit() throws Exception {
+ final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
+ boolean sawTheValidSequence = false;
+
+ for (; ; ) {
+ eventProcessor.startIteration();
+ Thread tb = new Thread(() -> {
+ RaceConditionTracker.onEvent("B1:enter");
+ RaceConditionTracker.onEvent("B1:exit");
+ RaceConditionTracker.onEvent("B2");
+ RaceConditionTracker.onEvent("B3:enter");
+ RaceConditionTracker.onEvent("B3:exit");
+ });
+ tb.start();
+
+ RaceConditionTracker.onEvent("A1");
+ RaceConditionTracker.onEvent("A2:enter");
+ RaceConditionTracker.onEvent("A2:exit");
+ RaceConditionTracker.onEvent("A3:enter");
+ RaceConditionTracker.onEvent("A3:exit");
+
+ tb.join();
+ final boolean needMoreIterations = eventProcessor.finishIteration();
+
+ sawTheValidSequence = sawTheValidSequence ||
+ "B1:enter|B1:exit|A1|A2:enter|A2:exit|B2|A3:enter|A3:exit|B3:enter|B3:exit".
+ equals(eventProcessor.getCurrentSequenceString());
+
+ if (!needMoreIterations) break;
+ }
+
+ assertEquals("Wrong number of leaf nodes",
+ factorial(3 + 3) / (factorial(3) * factorial(3)),
+ eventProcessor.numberOfLeafNodes());
+ assertTrue(sawTheValidSequence);
+ }
+
+ @Test
// 2 threads, 3 events each; reproducing a particular event sequence.
public void test3_3_ReproMode() throws Exception {
final RaceConditionReproducer eventProcessor = new RaceConditionReproducer(
@@ -122,4 +162,42 @@
factorial(2 + 2 + 1) / (factorial(2) * factorial(2) * factorial(1)),
eventProcessor.numberOfLeafNodes());
}
+
+ @Test
+ @Ignore // The test is too long for continuous testing.
+ // 2 threads with 2 events; 1 thread with 1 event. Includes enter-exit pairs.
+ public void test2_1_2_enter_exit() throws Exception {
+ final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
+
+ for (; ; ) {
+ eventProcessor.startIteration();
+ Thread tb = new Thread(() -> {
+ RaceConditionTracker.onEvent("B1:enter");
+ RaceConditionTracker.onEvent("B1:exit");
+ RaceConditionTracker.onEvent("B2:enter");
+ RaceConditionTracker.onEvent("B2:exit");
+ });
+ tb.start();
+
+ Thread tc = new Thread(() -> {
+ RaceConditionTracker.onEvent("C1:enter");
+ RaceConditionTracker.onEvent("C1:exit");
+ });
+ tc.start();
+
+ RaceConditionTracker.onEvent("A1:enter");
+ RaceConditionTracker.onEvent("A1:exit");
+ RaceConditionTracker.onEvent("A2:enter");
+ RaceConditionTracker.onEvent("A2:exit");
+
+ tb.join();
+ tc.join();
+
+ if (!eventProcessor.finishIteration()) break;
+ }
+
+ assertEquals("Wrong number of leaf nodes",
+ factorial(2 + 2 + 1) / (factorial(2) * factorial(2) * factorial(1)),
+ eventProcessor.numberOfLeafNodes());
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 84fd908..82ea8be 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -54,7 +54,7 @@
@NonNull
public AppIcon getAppIcon(String appName) {
final UiObject2 allAppsContainer = verifyActiveContainer();
- final BySelector appIconSelector = AppIcon.getAppIconSelector(appName);
+ final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
if (!allAppsContainer.hasObject(appIconSelector)) {
scrollBackToBeginning();
int attempts = 0;
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index efefc0d..3ffd30c 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -31,8 +31,8 @@
super(launcher, icon);
}
- static BySelector getAppIconSelector(String appName) {
- return By.clazz(TextView.class).text(appName).pkg(LauncherInstrumentation.LAUNCHER_PKG);
+ static BySelector getAppIconSelector(String appName, LauncherInstrumentation launcher) {
+ return By.clazz(TextView.class).text(appName).pkg(launcher.getLauncherPackageName());
}
/**
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
index 2a03f9a..7f28151 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
@@ -20,6 +20,8 @@
import androidx.test.uiautomator.UiObject2;
+import java.util.List;
+
/**
* Context menu of an app icon.
*/
@@ -37,10 +39,9 @@
* Returns a menu item with a given number. Fails if it doesn't exist.
*/
public AppIconMenuItem getMenuItem(int itemNumber) {
- assertTrue(mDeepShortcutsContainer.getChildCount() > itemNumber);
-
- final UiObject2 shortcut = mLauncher.waitForObjectInContainer(
- mDeepShortcutsContainer.getChildren().get(itemNumber), "bubble_text");
- return new AppIconMenuItem(mLauncher, shortcut);
+ final List<UiObject2> menuItems = mLauncher.getObjectsInContainer(mDeepShortcutsContainer,
+ "bubble_text");
+ assertTrue(menuItems.size() > itemNumber);
+ return new AppIconMenuItem(mLauncher, menuItems.get(itemNumber));
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 27e0954..08d2889 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -64,7 +64,7 @@
mLauncher.swipe(
navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
- navBar.getVisibleBounds().centerX(), height - 300);
+ navBar.getVisibleBounds().centerX(), height - 400);
} else {
mLauncher.getSystemUiObject("recent_apps").click();
}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 4fce211..5f60113 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -16,13 +16,13 @@
package com.android.launcher3.tapl;
-import java.util.Collections;
-import java.util.List;
-
import androidx.annotation.NonNull;
import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiObject2;
+import java.util.Collections;
+import java.util.List;
+
/**
* Common overview pane for both Launcher and fallback recents
*/
@@ -69,7 +69,7 @@
public OverviewTask getCurrentTask() {
verifyActiveContainer();
final List<UiObject2> taskViews = mLauncher.getDevice().findObjects(
- LauncherInstrumentation.getLauncherObjectSelector("snapshot"));
+ mLauncher.getLauncherObjectSelector("snapshot"));
LauncherInstrumentation.assertNotEquals("Unable to find a task", 0, taskViews.size());
// taskViews contains up to 3 task views: the 'main' (having the widest visible
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index bd1c657..49bd73a 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -41,6 +41,7 @@
import org.junit.Assert;
import java.lang.ref.WeakReference;
+import java.util.List;
import java.util.concurrent.TimeoutException;
/**
@@ -83,7 +84,6 @@
private static final String APPS_RES_ID = "apps_view";
private static final String OVERVIEW_RES_ID = "overview_panel";
private static final String WIDGETS_RES_ID = "widgets_list_view";
- static final String LAUNCHER_PKG = "com.google.android.apps.nexuslauncher";
public static final int WAIT_TIME_MS = 60000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
@@ -368,6 +368,11 @@
}
@NonNull
+ List<UiObject2> getObjectsInContainer(UiObject2 container, String resName) {
+ return container.findObjects(getLauncherObjectSelector(resName));
+ }
+
+ @NonNull
UiObject2 waitForObjectInContainer(UiObject2 container, String resName) {
final UiObject2 object = container.wait(
Until.findObject(getLauncherObjectSelector(resName)),
@@ -379,14 +384,18 @@
@NonNull
UiObject2 waitForLauncherObject(String resName) {
- final UiObject2 object = mDevice.wait(Until.findObject(getLauncherObjectSelector(resName)),
- WAIT_TIME_MS);
- assertNotNull("Can't find a launcher object; id: " + resName, object);
+ final BySelector selector = getLauncherObjectSelector(resName);
+ final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS);
+ assertNotNull("Can't find a launcher object; selector: " + selector, object);
return object;
}
- static BySelector getLauncherObjectSelector(String resName) {
- return By.res(LAUNCHER_PKG, resName);
+ BySelector getLauncherObjectSelector(String resName) {
+ return By.res(getLauncherPackageName(), resName);
+ }
+
+ String getLauncherPackageName() {
+ return mDevice.getLauncherPackageName();
}
@NonNull
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index c45f0f0..5e6ad4d 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -69,7 +69,7 @@
@Nullable
public AppIcon tryGetWorkspaceAppIcon(String appName) {
final UiObject2 workspace = verifyActiveContainer();
- final UiObject2 icon = workspace.findObject(AppIcon.getAppIconSelector(appName));
+ final UiObject2 icon = workspace.findObject(AppIcon.getAppIconSelector(appName, mLauncher));
return icon != null ? new AppIcon(mLauncher, icon) : null;
}
@@ -85,7 +85,7 @@
return new AppIcon(mLauncher,
mLauncher.getObjectInContainer(
verifyActiveContainer(),
- AppIcon.getAppIconSelector(appName)));
+ AppIcon.getAppIconSelector(appName, mLauncher)));
}
/**
@@ -108,7 +108,7 @@
@NonNull
private AppIcon getHotseatAppIcon(String appName) {
return new AppIcon(mLauncher, mLauncher.getObjectInContainer(
- mHotseat, AppIcon.getAppIconSelector(appName)));
+ mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
}
private void dragIconToNextScreen(AppIcon app, UiObject2 workspace) {